電気設備は機械設備の電源を用意しますが、その確認は電気設計者が空調・衛生図面を見ながら動力やコンセントを配置しているのが実態です。電気設計者の間違い、空調・衛生設計者の間違い、あるいは変更など様々な原因で設備間で不整合が生じることがあります。
Revitアドインを使えば、両設備間の対応関係を瞬時にチェックすることができます。
そのサンプルとして、空調機器に対する動力、コンセントの整合性をチェックするアドインを紹介します。
Contents
プラン
ほとんどが天井カセット型の空調機で、それに対して電気で天井内に単相200Vコンセントを設置するというごくありふれた内容です。一般的には別モデルでリンクしていますが、このサンプルでは単純な例とするために機械設備と電気設備を同じモデルに入力している点をご承知ください。

チェックのステップ
- 電気モデルから目的の動力、コンセント電源を全て抽出
- 機械モデルから目的の機械設備機器を全て抽出
- 電気と機械で、機器番号、機器名、電気種別、容量を比較する。
- 整合状態を判定し不整合箇所を黄色セルにしてExcelに出力する
整合させるファミリ・タイプ
まず整合させるファミリとタイプを限定しておきます。何種類でも指定できます。電気、機械とも取得したインスタンスを入れるリストを用意しておきます。elecInstancesとmechInstancesです。
//条件***************
//電気設備で対象とする[ファミリ名,シンボル名]
string[] elecTarget = new string[] { "20301_鍵付コンセント_露出型_200V", "20308_ジョイントボックス_強電用" };
//電気対象要素のリスト
List<ElecEquip> elecInstances = new List<ElecEquip>();
//機械設備で対象とするファミリ名とシンボル名
string[] mechTarget = new string[] { "07052_PAC-CK4_室内機_カセット形(4方向)", "07062_ACP-FRV(J)_室内機_床置(露出)立形(直吹)" };
//機械対象要素のリスト
List<MechEquip> mechInstances = new List<MechEquip>();
//*******************目的の電気インスタンス取得
まずFilteredElementCollectorで電気器具インスタンスを全て抽出します。電気器具のカテゴリはRevit APIではBuiltInCategory.OST_ElectricalFixturesです。
次にQuerryを使って、電気器具の中からファミリ、タイプのものだけを抽出します。これで残ったものがelecInstancesに入ります。これが電気の目的のインスタンスです。
//電気インスタンスを全て抽出する
FilteredElementCollector elecCol = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_ElectricalFixtures);
//電気の対象ファミリ、タイプを絞る
for (int i = 0; i < elecTarget.Length; i++)
{
//Querryで要素絞る
var query = from element in elecCol.Cast<FamilyInstance>()
where (element.Symbol.Family.Name == elecTarget[i])
select element;
//抽出したものをリストにする
List<FamilyInstance> elecFamilyInstances = query.ToList<FamilyInstance>();
foreach(FamilyInstance elecInstance in elecFamilyInstances)
{
ElecEquip eEquip = new ElecEquip(elecInstance);
elecInstances.Add(eEquip);
}
}目的の空調インスタンスの取得
空調も電気と同様の方法で抽出します。
//機械設備機器を全て抽出する
//インスタンスを全て抽出する
FilteredElementCollector mechCol = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_MechanicalEquipment);
//機械の対象ファミリ、タイプを絞る
for (int i = 0; i < mechTarget.Length; i++)
{
//Querryで要素絞る
var query = from element in mechCol.Cast<FamilyInstance>()
where (element.Symbol.Family.Name == mechTarget[i])
select element;
//抽出したものをリストにする
List<FamilyInstance> mechFamilyInstances = query.ToList<FamilyInstance>();
foreach (FamilyInstance mechInstance in mechFamilyInstances)
{
MechEquip mEquip = new MechEquip(mechInstance);
mechInstances.Add(mEquip);
}
}機械と電気のインスタンスのプロパティを表形式のDataTableに並べる
foreach (MechEquip eqp in mechInstances)で機械のインスタンスのプロパティを1つずつTableDataに入れていきます。
その過程でforeach (ElecEquip elecEquip in elecInstances)のところでその機械設備インスタンスに対応する電気インスタンスがあるかどうか調べて、あったら同じ行の電気の部分にプロパティを入れていきます。
ちょっと工夫したのは機械インスタンスと対応する電気インスタンスの距離をmm単位で計算している点です。計算しているのはDistanceメソッドです。あまり離れていると良くないですからね。
ここで対処できていない課題もあります。機械インスタンスに対応していない電気インスタンスは売れ残ってしまいDataTableには登録されません。ここは今後の対応です。
//電気と機械で、機器番号、機器名、電気種別、容量を比較する。
//「機器記号」をキーとして機械と電気の対応関係を確認する
//表形式のデータを扱うためにDataTableを作成する
var datatable = new DataTable("tblData");
//列名、型を設定する
datatable.Columns.Add("M 記号", typeof(string));//0
datatable.Columns.Add("M ファミリ名", typeof(string));//1
datatable.Columns.Add("M タイプ名", typeof(string));//2
datatable.Columns.Add("M 極数", typeof(int));//3
datatable.Columns.Add("M 相", typeof(int));//4
datatable.Columns.Add("M 電圧", typeof(double));//5
datatable.Columns.Add("M 消費電力_冷房", typeof(double));//6
datatable.Columns.Add("M 消費電力_暖房", typeof(double));//7
datatable.Columns.Add("M 位置座標", typeof(string));//8
datatable.Columns.Add("E 記号", typeof(string));//9
datatable.Columns.Add("E ファミリ名", typeof(string));//10
datatable.Columns.Add("E タイプ名", typeof(string));//11
datatable.Columns.Add("E 極数", typeof(int));//12
datatable.Columns.Add("E 相", typeof(int));//13
datatable.Columns.Add("E 電圧", typeof(double));//14
datatable.Columns.Add("E 皮相電力", typeof(double));//15
datatable.Columns.Add("E 位置座標", typeof(string));//16
datatable.Columns.Add("距離mm", typeof(double));//17
//機器リストからテーブル入力
foreach (MechEquip eqp in mechInstances)
{
var row = datatable.NewRow();
//機械設備のデータ入力 、パラメータが存在しない場合のエラーをもっと考慮する必要ある
if (eqp.Kigou != null) { row[0] = eqp.Kigou; } else { row[0] = DBNull.Value; }
if (eqp.Family != null) { row[1] = eqp.Family; } else { row[1] = DBNull.Value; }
if (eqp.Type != null) { row[2] = eqp.Type; } else { row[2] = DBNull.Value; }
if (eqp.Pole != null) { row[3] = eqp.Pole; } else { row[3] = DBNull.Value; }
if (eqp.Phase != null) { row[4] = eqp.Phase; } else { row[4] = DBNull.Value; }
row[5] = Math.Round(UnitUtils.ConvertFromInternalUnits(KUtil.tryDouble(eqp.Voltage), UnitTypeId.Volts), 0);
row[6] = Math.Round(UnitUtils.ConvertFromInternalUnits(KUtil.tryDouble(eqp.ReibouShouhidenryoku), UnitTypeId.Watts), 0);
row[7] = Math.Round(UnitUtils.ConvertFromInternalUnits(KUtil.tryDouble(eqp.DanbouShouhidenryoku), UnitTypeId.Watts), 0);
row[8] = eqp.Loation.ToString();
//対応する電気があったらデータを代入、パラメータが存在しない場合のエラーをもっと考慮する必要ある、対応ないものの処理考慮必要
foreach (ElecEquip elecEquip in elecInstances)
{
if(elecEquip.Kigou != null)
{
if (elecEquip.Kigou == eqp.Kigou)
{
if (elecEquip.Kigou != null) { row[9] = elecEquip.Kigou; } else { row[9] = DBNull.Value; }
if (elecEquip.Family != null) { row[10] = elecEquip.Family; } else { row[10] = DBNull.Value; }
if (elecEquip.Type != null) { row[11] = elecEquip.Type; } else { row[11] = DBNull.Value; }
if (elecEquip.Pole != null) { row[12] = elecEquip.Pole; } else { row[12] = DBNull.Value; }
if (elecEquip.Phase != null) { row[13] = elecEquip.Phase; } else { row[13] = DBNull.Value; }
row[14] = UnitUtils.ConvertFromInternalUnits(KUtil.tryDouble(elecEquip.Voltage), UnitTypeId.Volts);
row[15] = Math.Round(UnitUtils.ConvertFromInternalUnits(KUtil.tryDouble(elecEquip.Hisoudenryoku), UnitTypeId.Watts), 0);
row[16] = elecEquip.Loation.ToString();
row[17] = Distance(row[8].ToString(), row[16].ToString());//距離
}
}
}
datatable.Rows.Add(row);
}
//2点間の距離を計算する
private double Distance(string obj1, string obj2)
{
double distance;
obj1 = obj1.Trim().Replace("(", "").Replace(")", "");
obj2 = obj2.Trim().Replace("(", "").Replace(")", "");
string[] obj11 = obj1.Split(',');
double x1 = double.Parse(obj11[0]);double y1 = double.Parse(obj11[1]);double z1 = double.Parse(obj11[2]);
string[] obj21 = obj2.Split(',');
double x2 = double.Parse(obj21[0]);double y2 = double.Parse(obj21[1]);double z2= double.Parse(obj21[2]);
distance = Math.Sqrt(Math.Abs(x1 - x2)* Math.Abs(x1 - x2) + Math.Abs(y1 - y2) * Math.Abs(y1 - y2)+ Math.Abs(z1 - z2) * Math.Abs(z1 - z2));
distance = UnitUtils.ConvertFromInternalUnits(distance, UnitTypeId.Millimeters);//ミリメーター
distance = Math.Round(distance);
return distance;
}Excelに整合性情報を書き出す
次のコードはDataTableをExcelシートに流し込んで、そのあとに機械設備のプロパティと対応する電気設備のプロパティを同じ行で比較して、違うセルは黄色に着色しています。
比較しているところは、//整合性をチェックする、違うところはセルを黄色にするのfor文です。各if文でPole、相、電圧、電力、距離です。距離は3000mm超離れていたら不整合としています。
//次に、室一覧をExcelに保存するためのダイアログを開く
System.Windows.Forms.SaveFileDialog saveFileDialog = KUtil.SaveExcel();
String filename = saveFileDialog.FileName;
//保存ファイル名が正常に入力されていればExcel保存実行
if (filename != "")
{
//ユーザーが指定したExcelファイル名でファイルストリームを取得する
FileStream fs = (FileStream)saveFileDialog.OpenFile();
//ExcelPackageを作成する
using (ExcelPackage package = new ExcelPackage(fs))
{
//ExcelのワークシートにRoomsという名称のシートを作る
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Seigousei");
//Excelの一番左上の位置A1からDataTableを一気に流し込む
worksheet.Cells["A1"].LoadFromDataTable(datatable, true);
//データの範囲を取得する
int maxRow = worksheet.Dimension.Rows;
int maxColomun = worksheet.Dimension.Columns;
//整合性をチェックする、違うところはセルを黄色にする
for (int i = 2; i <= maxRow; i++)//インデックスに注意 データは2行目から存在する
{
//Pole
if (worksheet.Cells[i, 4].Value.ToString() != worksheet.Cells[i, 13].Value.ToString())
{
CellColor(worksheet, i, 13, System.Drawing.Color.Yellow);
}
//相
if (worksheet.Cells[i, 5].Value != worksheet.Cells[i, 14].Value)
{
CellColor(worksheet, i, 14, System.Drawing.Color.Yellow);
}
//電圧
if (worksheet.Cells[i, 6].Value.ToString() != worksheet.Cells[i, 15].Value.ToString())
{
CellColor(worksheet, i, 15, System.Drawing.Color.Yellow);
}
//電力
double reibouWatt = Convert.ToDouble(worksheet.Cells[i, 7].Value.ToString());
double danbouWatt = Convert.ToDouble(worksheet.Cells[i, 8].Value.ToString());
double denkiShouhi = Convert.ToDouble(worksheet.Cells[i, 16].Value.ToString());
if (Math.Max(reibouWatt, danbouWatt) != denkiShouhi)
{
CellColor(worksheet, i, 16, System.Drawing.Color.Yellow);
}
//距離
if (Convert.ToDouble(worksheet.Cells[i, 18].Value.ToString()) > 3000 )
{
CellColor(worksheet, i, 18, System.Drawing.Color.Yellow);
}
}
//列幅はオートフィットさせる。最後に実行したほうがよい
for (int i = 0; i < maxColomun; i++)
{
worksheet.Column(i + 1).AutoFit();
worksheet.Column(i + 1).Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
}
//Excel編集の終了 Excelファイルを保存する
package.Save();
//正常時にメッセージを出す
TaskDialog.Show("正常終了", "Excel出力は正常終了。この後Excelファイルを開きます。");
//確認のためにExcelを起動して保存したファイルを開く
Process process = new Process();
process.StartInfo.FileName = "excel.exe";
process.StartInfo.Arguments = saveFileDialog.FileName;
process.Start();
}
}整合結果
天井カセットの室内機とコンセントの照合です。不整合は黄色になっていますが、極数、電圧が違うのが多い。機械設備側は天カセが極数3、三相、200Vになっていますが、電気のほうは極数2、電圧100Vになっています。両方に間違いがあると思います。天カセなので一般的には単相200Vです。なので機械側は極数2、相は1とすべきですが間違っています。電気側は極数2は合っていますが、電圧は200Vにすべきです。これらはRUGのファミリをそのまま使いましたが、どうも適切に設定されていないものがあるようです。電力は違うところが少しありました。これはモデリングのときのミスです。

まとめ
整合性確認は機器がたくさんあると大変ですが、このような方法を使えばだいぶ楽になると思います
興味のある方はソースコードについてはこちらを参照ください
githubはこちらhttps://github.com/katsumikawasaki/Seigousei.git
(ご注意)ここに掲載したコードはひとつの技術的な解法を示すサンプルです。実践的な用途に耐えうる堅牢性や品質等は備えていません。コードの実行結果について当組織は一切責任を負いませんので、参照、利用はご自身の責任でお願いします。
書籍もぜひご覧ください

コメントを残す