콘텐츠로 이동

ODB++ 예제와 읽는 코드

ODB++ 는 텍스트 파일로 이루어진 디렉터리 트리다. 열어서 읽을 수 있고, 읽으면 구조가 그대로 드러난다.

myboard/
├── matrix/
│ └── matrix 층의 이름 · 종류 · 순서
├── steps/
│ └── board/
│ ├── stephdr 스텝의 머리 — 원점과 크기
│ ├── profile 보드 외곽
│ ├── layers/
│ │ ├── top/features 앞면 구리의 형상
│ │ ├── bot/features 뒷면 구리의 형상
│ │ └── drill/features 홀
│ ├── eda/
│ │ └── data 넷 연결
│ └── components_top 앞면 부품
├── symbols/ 사용자 심볼
├── fonts/ 문자 정의
└── misc/
└── info 작업 정보

층 하나가 디렉터리 하나이고 그 안의 형상 파일 이름은 늘 같다. 파일 이름이 뜻을 정하지 않으므로 받는 쪽이 이름 규칙을 알 필요가 없다.

#
#Layer features
#
$0 c0.2
$1 r0.8x1.2
#
L 10.0 5.0 30.0 5.0 0 P 0
L 30.0 5.0 30.0 25.0 0 P 0
P 12.0 8.0 1 P 0 0
S P 0
OB 40.0 10.0 I
OS 60.0 10.0
OS 60.0 30.0
OS 40.0 30.0
OE
SE
$0 c0.20 번 심볼(symbol)은 지름 0.2 의 원이다
$1 r0.8x1.21 번 심볼은 0.8 × 1.2 의 사각이다
L 10.0 5.0 30.0 5.0 0 P 0(10,5) 에서 (30,5) 까지 이어지는 0 번 심볼의 선 피처(feature) · 극성(polarity) P
L 30.0 5.0 30.0 25.0 0 P 0이어지는 구간 — 트랙(track)이 꺾인 자리
P 12.0 8.0 1 P 0 0(12,8)1 번 심볼로 배치한 패드(pad)
S P 0채움면 시작
OB 40.0 10.0 I윤곽 시작점
OS …윤곽의 다음 점
OE SE윤곽 끝 · 채움면 끝

선 줄에 굵기가 없다. 굵기는 $0 이 정하고 줄은 번호만 가리킨다. 같은 굵기의 트랙 수천 개가 번호 하나를 공유하므로 파일이 작아지고, 굵기를 바꾸면 한 곳만 고치면 된다.

H optimize Y
#
NET GND
NET VBUS
#
0 0.35 12.0 8.0 T e c
1 0.35 30.0 25.0 T m c v
H optimize Y머리 — 자료가 정리된 상태인가
NET GND넷 이름 목록. 뒤의 줄이 번호로 이것을 가리킨다
0 0.35 12.0 8.0 T e c0 번 넷의 단자 — 반지름 0.35 · 좌표 · 앞면 · 끝점 · 노출됨
1 0.35 30.0 25.0 T m c v1 번 넷의 단자 — 중간점 · 비아

넷마다 어느 좌표에 어떤 단자가 있는지 적히므로, 받는 쪽은 형상에서 연결을 추출하지 않는다.

4. 레코드를 갈라 읽는 최소 코드

섹션 제목: “4. 레코드를 갈라 읽는 최소 코드”

피처 파일은 첫 글자로 갈래가 정해지는 줄의 나열이다. 읽는 쪽의 첫 단계는 주석과 심볼 정의를 걸러 내고 갈래를 나누는 일이다.

#include <string>
#include <string_view>
#include <vector>
namespace codename7
{
/// ODB++ 피처 파일의 한 줄.
struct s_odb_record
{
char kind = 0; ///< L · P · A · S · T · B, 심볼 정의는 '$'
std::vector<std::string> fields; ///< 공백으로 가른 나머지
};
class c_odb_feature_reader
{
public:
/// 주석과 빈 줄을 걸러 내고 레코드로 만든다.
static std::vector<s_odb_record> Parse(std::string_view aText)
{
std::vector<s_odb_record> out;
size_t pos = 0;
while (pos <= aText.size())
{
const size_t nl = aText.find('\n', pos);
std::string_view line = aText.substr(pos, (nl == std::string_view::npos ? aText.size() : nl) - pos);
pos = (nl == std::string_view::npos) ? aText.size() + 1 : nl + 1;
line = trim(line);
if (line.empty() || line.front() == '#') continue; // 주석과 빈 줄
s_odb_record rec;
rec.kind = line.front();
rec.fields = split(line.substr(1));
out.push_back(std::move(rec));
}
return out;
}
private:
static std::string_view trim(std::string_view s)
{
while (!s.empty() && isBlank(s.front())) s.remove_prefix(1);
while (!s.empty() && isBlank(s.back())) s.remove_suffix(1);
return s;
}
static bool isBlank(char c) { return c == ' ' || c == '\t' || c == '\r'; }
static std::vector<std::string> split(std::string_view s)
{
std::vector<std::string> out;
size_t i = 0;
while (i < s.size())
{
while (i < s.size() && isBlank(s[i])) ++i;
const size_t start = i;
while (i < s.size() && !isBlank(s[i])) ++i;
if (i > start) out.emplace_back(s.substr(start, i - start));
}
return out;
}
};
}

갈래를 나눈 뒤에는 종류마다 필드를 해석한다.

종류필드 순서
L시작 x · 시작 y · 끝 x · 끝 y · 심볼 번호 · 극성 · 코드
Px · y · 모양 참조 · 극성 · 코드 · 방향
S극성 · 코드 (뒤이어 윤곽, SE 로 닫는다)
$심볼 번호와 정의
순서하는 일
1층 목록을 열어 순서와 종류를 확인한다
2각 층의 피처 파일이 비어 있지 않은지 본다
3넷 자료의 넷 수를 설계와 대조한다
4부품 수와 면 배분을 대조한다
5압축한 묶음을 다시 풀어 트리가 온전한지 본다

3 번이 이 형식에서 새로 생기는 검증이다. 형상과 넷이 함께 담기므로, 둘이 서로 맞는지가 확인 대상이 된다.