検査レシピ=処理/検査ステップの縦リスト(配線なし)。yfoxlib(yfoximg/yfoxbase)使用。 - ビジュアル・パイプライン(カード+適用後サムネイル+緑/赤 合否) - テンプレート/対話式ウィザードでレシピ生成(CTaskDialog) - パラメータのスライダー・ライブ調整 - レシピ保存/読込(*.itr) + 最近使ったファイル(MRU) - 検査範囲(ROI)を複数マウス指定(ドラッグ追加/右クリック削除) - カードのドラッグ並べ替え - 判定結果画像の保存 + 検査ログCSV(項目拡張) - 処理6種+検査3種(明るさ/白画素率/塊の数) - /selftest(実行+レシピ往復+結果画像+複数ROI塊カウント検証)、/demo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
135 lines
3.4 KiB
C++
135 lines
3.4 KiB
C++
//
|
|
// RecipeStore.cpp - レシピの保存/読込。行ベースの UTF-8 テキスト。
|
|
//
|
|
// ImgTestSystem Recipe v1
|
|
// ROI <enabled> <l> <t> <r> <b>
|
|
// STEP <kindId>
|
|
// PARAM <key> <value>
|
|
// ...
|
|
//
|
|
#include "stdafx.h"
|
|
#include "RecipeStore.h"
|
|
#include "StepCatalog.h"
|
|
|
|
namespace core {
|
|
|
|
namespace {
|
|
|
|
void WriteUtf8(CFile& f, const CString& s)
|
|
{
|
|
int n = WideCharToMultiByte(CP_UTF8, 0, s, s.GetLength(), NULL, 0, NULL, NULL);
|
|
if (n <= 0) return;
|
|
CStringA a;
|
|
WideCharToMultiByte(CP_UTF8, 0, s, s.GetLength(), a.GetBuffer(n), n, NULL, NULL);
|
|
a.ReleaseBuffer(n);
|
|
f.Write((LPCSTR)a, a.GetLength());
|
|
}
|
|
|
|
CString ReadAllUtf8(LPCWSTR path)
|
|
{
|
|
CFile f;
|
|
if (!f.Open(path, CFile::modeRead | CFile::shareDenyWrite)) return L"";
|
|
ULONGLONG len = f.GetLength();
|
|
if (len == 0 || len > (50 * 1024 * 1024)) return L"";
|
|
CStringA a;
|
|
UINT got = f.Read(a.GetBuffer((int)len), (UINT)len);
|
|
a.ReleaseBuffer(got);
|
|
// BOM 除去
|
|
if (a.GetLength() >= 3 && (BYTE)a[0] == 0xEF && (BYTE)a[1] == 0xBB && (BYTE)a[2] == 0xBF)
|
|
a = a.Mid(3);
|
|
int wn = MultiByteToWideChar(CP_UTF8, 0, a, a.GetLength(), NULL, 0);
|
|
CString w;
|
|
MultiByteToWideChar(CP_UTF8, 0, a, a.GetLength(), w.GetBuffer(wn), wn);
|
|
w.ReleaseBuffer(wn);
|
|
return w;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool SaveRecipe(LPCWSTR path, const Recipe& r)
|
|
{
|
|
CFile f;
|
|
if (!f.Open(path, CFile::modeCreate | CFile::modeWrite)) return false;
|
|
// UTF-8 BOM
|
|
const unsigned char bom[3] = { 0xEF, 0xBB, 0xBF };
|
|
f.Write(bom, 3);
|
|
|
|
WriteUtf8(f, L"ImgTestSystem Recipe v1\r\n");
|
|
for (const CRect& roi : r.rois)
|
|
{
|
|
CString line;
|
|
line.Format(L"ROI %d %d %d %d\r\n", (int)roi.left, (int)roi.top, (int)roi.right, (int)roi.bottom);
|
|
WriteUtf8(f, line);
|
|
}
|
|
|
|
for (const Step& s : r.steps)
|
|
{
|
|
WriteUtf8(f, L"STEP " + StepKindId(s.kind) + L"\r\n");
|
|
for (const StepParam& p : s.params)
|
|
{
|
|
CString pl;
|
|
pl.Format(L"PARAM %s %.6g\r\n", (LPCWSTR)p.key, p.value);
|
|
WriteUtf8(f, pl);
|
|
}
|
|
}
|
|
f.Close();
|
|
return true;
|
|
}
|
|
|
|
bool LoadRecipe(LPCWSTR path, Recipe& out)
|
|
{
|
|
CString text = ReadAllUtf8(path);
|
|
if (text.IsEmpty()) return false;
|
|
|
|
Recipe r;
|
|
bool sawHeader = false;
|
|
Step* cur = nullptr;
|
|
|
|
int pos = 0;
|
|
while (pos < text.GetLength())
|
|
{
|
|
int nl = text.Find(L'\n', pos);
|
|
CString line = (nl < 0) ? text.Mid(pos) : text.Mid(pos, nl - pos);
|
|
pos = (nl < 0) ? text.GetLength() : nl + 1;
|
|
line.TrimRight(L"\r");
|
|
line.TrimLeft();
|
|
if (line.IsEmpty()) continue;
|
|
|
|
if (line.Find(L"ImgTestSystem Recipe") == 0) { sawHeader = true; continue; }
|
|
|
|
if (line.Left(4) == L"ROI ")
|
|
{
|
|
// 新形式: ROI l t r b(4値) / 旧形式: ROI enabled l t r b(5値)
|
|
int a = 0, bb = 0, c = 0, d = 0, e = 0;
|
|
int got = swscanf_s((LPCWSTR)line, L"ROI %d %d %d %d %d", &a, &bb, &c, &d, &e);
|
|
if (got == 5) { if (a != 0) r.rois.push_back(CRect(bb, c, d, e)); }
|
|
else if (got == 4) { r.rois.push_back(CRect(a, bb, c, d)); }
|
|
continue;
|
|
}
|
|
if (line.Left(5) == L"STEP ")
|
|
{
|
|
CString id = line.Mid(5); id.Trim();
|
|
StepKind k;
|
|
if (StepKindFromId(id, k)) { r.steps.push_back(MakeStep(k)); cur = &r.steps.back(); }
|
|
else cur = nullptr;
|
|
continue;
|
|
}
|
|
if (line.Left(6) == L"PARAM " && cur)
|
|
{
|
|
CString rest = line.Mid(6); rest.Trim();
|
|
int sp = rest.Find(L' ');
|
|
if (sp <= 0) continue;
|
|
CString key = rest.Left(sp);
|
|
double val = _wtof(rest.Mid(sp + 1));
|
|
cur->SetParam(key, val); // 既定パラメータのうち一致キーのみ上書き
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (r.steps.empty() && !sawHeader) return false;
|
|
out = r;
|
|
return true;
|
|
}
|
|
|
|
} // namespace core
|