20#include <boost/test/unit_test.hpp>
45#include <wx/filename.h>
52static std::vector<uint8_t> loadMinimalV13()
56 return { std::istreambuf_iterator<char>( file ), std::istreambuf_iterator<char>() };
60static std::vector<uint8_t> loadBinaryFixture(
const std::string& aName )
64 return { std::istreambuf_iterator<char>( file ), std::istreambuf_iterator<char>() };
68static uint32_t readU32(
const std::vector<uint8_t>& aBytes,
size_t aOffset )
70 return static_cast<uint32_t
>( aBytes[aOffset] ) | (
static_cast<uint32_t
>( aBytes[aOffset + 1] ) << 8 )
71 | (
static_cast<uint32_t
>( aBytes[aOffset + 2] ) << 16 )
72 | (
static_cast<uint32_t
>( aBytes[aOffset + 3] ) << 24 );
76static uint16_t readU16(
const std::vector<uint8_t>& aBytes,
size_t aOffset )
78 return static_cast<uint16_t
>( aBytes[aOffset] ) | (
static_cast<uint16_t
>( aBytes[aOffset + 1] ) << 8 );
82static void writeU16( std::vector<uint8_t>& aBytes,
size_t aOffset, uint16_t aValue )
84 aBytes[aOffset] =
static_cast<uint8_t
>( aValue );
85 aBytes[aOffset + 1] =
static_cast<uint8_t
>( aValue >> 8 );
89static void writeU32( std::vector<uint8_t>& aBytes,
size_t aOffset, uint32_t aValue )
91 writeU16( aBytes, aOffset,
static_cast<uint16_t
>( aValue ) );
92 writeU16( aBytes, aOffset + 2,
static_cast<uint16_t
>( aValue >> 16 ) );
96static size_t outerControllerOffset(
const std::vector<uint8_t>& aBytes,
size_t aController )
98 size_t offset = 0x254;
100 for(
size_t controller = 1; controller < aController; ++controller )
101 offset += readU32( aBytes, 0x20 + controller * 28 + 12 );
107static size_t sheetControllerOffset(
const std::vector<uint8_t>& aBytes,
size_t aController )
109 const size_t sheetOffset = readU32( aBytes, outerControllerOffset( aBytes, 3 ) );
110 size_t offset = sheetOffset + 20 + 24 * 28;
112 for(
size_t controller = 1; controller < aController; ++controller )
113 offset += readU32( aBytes, sheetOffset + 20 + ( controller - 1 ) * 28 + 16 );
119static uint32_t sheetControllerCount(
const std::vector<uint8_t>& aBytes,
size_t aController )
121 const size_t sheetOffset = readU32( aBytes, outerControllerOffset( aBytes, 3 ) );
122 return readU32( aBytes, sheetOffset + 20 + ( aController - 1 ) * 28 + 12 );
126template <
typename Item>
127static const Item& itemNamed(
const std::vector<Item>& aItems,
const wxString& aName )
129 auto item = std::ranges::find_if( aItems,
130 [&](
const Item& aItem )
132 return aItem.name.text == aName;
139static wxString propertyValue(
const std::vector<SOURCE_PROPERTY>& aProperties,
const wxString& aName )
141 auto property = std::ranges::find_if( aProperties,
144 return aProperty.
name.
text == aName;
147 return property->value.text;
153 std::ostringstream out;
159 out <<
':' << aSource.version <<
',' << aSource.objectClass <<
',' << aSource.controller <<
','
160 << aSource.recordIndex <<
',' << aSource.absoluteOffset <<
',' << aSource.length <<
',' << aSource.sheet;
166 provenance( sheet.
source );
170 <<
"|placements=" << aModel.
placements.size() <<
"|nets=" << aModel.
nets.size()
171 <<
"|buses=" << aModel.
buses.size() <<
"|labels=" << aModel.
labels.size()
172 <<
"|junctions=" << aModel.
junctions.size() <<
"|texts=" << aModel.
texts.size()
178enum class CANONICAL_KIND
201using CANONICAL_VALUE = std::variant<int64_t, bool, std::string, std::vector<std::string>>;
203struct CANONICAL_PROPERTY
205 CANONICAL_VALUE value;
207 bool operator==(
const CANONICAL_PROPERTY& )
const =
default;
208 bool operator<(
const CANONICAL_PROPERTY& aOther )
const
210 return std::tie( value, disposition ) < std::tie( aOther.value, aOther.disposition );
214struct CANONICAL_POINT
216 int64_t xHalfMils = 0;
217 int64_t yHalfMils = 0;
218 bool operator==(
const CANONICAL_POINT& )
const =
default;
219 bool operator<(
const CANONICAL_POINT& aOther )
const
221 return std::tie( xHalfMils, yHalfMils ) < std::tie( aOther.xHalfMils, aOther.yHalfMils );
225struct CANONICAL_GEOMETRY
227 std::vector<CANONICAL_POINT> points;
228 std::optional<int64_t> angleTenths;
229 bool operator==(
const CANONICAL_GEOMETRY& )
const =
default;
232struct CANONICAL_SEMANTIC_RECORD
235 CANONICAL_KIND kind = CANONICAL_KIND::SETTINGS;
236 std::string stableKey;
237 std::map<std::string, CANONICAL_PROPERTY> properties;
238 CANONICAL_GEOMETRY geometry;
239 bool operator==(
const CANONICAL_SEMANTIC_RECORD& )
const =
default;
240 bool operator<(
const CANONICAL_SEMANTIC_RECORD& aOther )
const
242 return std::tie( sheet, kind, stableKey, properties, geometry.points, geometry.angleTenths )
243 < std::tie( aOther.sheet, aOther.kind, aOther.stableKey, aOther.properties, aOther.geometry.points,
244 aOther.geometry.angleTenths );
248using SNAPSHOT_ALLOWLIST = std::set<std::pair<std::string, PROPERTY_DISPOSITION>>;
250static CANONICAL_POINT point(
const SOURCE_POINT& aPoint )
252 return { aPoint.
x, aPoint.
y };
256 return {
static_cast<int64_t
>( std::llround( aPoint.
x * 2.0 ) ),
257 static_cast<int64_t
>( std::llround( aPoint.
y * 2.0 ) ) };
260static void addSourceProperties( CANONICAL_SEMANTIC_RECORD& aRecord,
const std::vector<SOURCE_PROPERTY>& aProperties,
261 const std::string& aPrefix = {} )
264 aRecord.properties[aPrefix +
property.name.text.ToStdString()] = {
property.value.text.ToStdString(),
265 property.disposition };
268static CANONICAL_PROPERTY unknownEnum( int64_t aValue )
285 return unknownEnum(
static_cast<int64_t
>( aKind ) );
299 return unknownEnum(
static_cast<int64_t
>( aKind ) );
304 if( std::ranges::any_of( aGraphic.
points,
307 return aPoint.arc.has_value();
316 return canonicalGraphicType( aGraphic.
type );
330 return unknownEnum(
static_cast<int64_t
>( aStyle ) );
333static CANONICAL_PROPERTY canonicalLineStyle(
int aStyle )
335 switch(
static_cast<int8_t
>( aStyle & 0xFF ) )
338 case -1:
return {
"solid" };
340 case -2:
return {
"dash" };
341 case -3:
return {
"dot" };
342 case -4:
return {
"dash_dot" };
343 case -5:
return {
"dash_dot_dot" };
344 default:
return unknownEnum( aStyle );
357 return unknownEnum(
static_cast<int64_t
>( aFill ) );
360static CANONICAL_PROPERTY canonicalFill(
bool aFilled )
362 return { aFilled ?
"filled" :
"none" };
365static CANONICAL_PROPERTY canonicalPinType( uint32_t aType )
369 case 0:
return {
"passive" };
370 case 1:
return {
"input" };
371 case 2:
return {
"output" };
372 case 3:
return {
"bidirectional" };
373 case 4:
return {
"tristate" };
374 case 5:
return {
"open_collector" };
375 case 6:
return {
"open_emitter" };
376 case 7:
return {
"power" };
377 case 8:
return {
"unspecified" };
378 default:
return unknownEnum( aType );
397 return unknownEnum(
static_cast<int64_t
>( aType ) );
400static CANONICAL_PROPERTY canonicalPinType(
char aType )
404 case 'U':
return {
"passive" };
405 case 'L':
return {
"input" };
406 case 'S':
return {
"output" };
407 case 'B':
return {
"bidirectional" };
408 case 'T':
return {
"tristate" };
409 case 'C':
return {
"open_collector" };
410 case 'E':
return {
"open_emitter" };
412 case 'G':
return {
"power" };
413 default:
return {
"unspecified" };
417static CANONICAL_PROPERTY canonicalPinStyle( uint32_t aStyle )
421 case 0:
return {
"normal" };
422 case 1:
return {
"inverted" };
423 case 2:
return {
"clock" };
424 case 3:
return {
"inverted_clock" };
425 default:
return unknownEnum( aStyle );
429static CANONICAL_PROPERTY canonicalPinStyle(
bool aInverted,
bool aClock )
431 return canonicalPinStyle( ( aInverted ? 1U : 0U ) | ( aClock ? 2U : 0U ) );
436 switch( aJustification )
443 return unknownEnum(
static_cast<int64_t
>( aJustification ) );
446static CANONICAL_PROPERTY canonicalVerticalJustification(
MODEL_JUSTIFICATION aJustification )
448 switch( aJustification )
455 return unknownEnum(
static_cast<int64_t
>( aJustification ) );
458static CANONICAL_PROPERTY canonicalHorizontalJustification(
int aJustification )
460 const int justification = aJustification & 0xFF;
461 const int horizontal = justification >= 8 ? justification - 8
462 : justification >= 2 ? justification - 2
468 case 0:
return {
"left" };
469 case 1:
return {
"right" };
470 case 4:
return {
"center" };
474static CANONICAL_PROPERTY canonicalVerticalJustification(
int aJustification )
476 if( aJustification >= 8 )
479 if( aJustification >= 2 )
482 if( aJustification >= 0 )
485 return unknownEnum( aJustification );
501 return unknownEnum(
static_cast<int64_t
>( aKind ) );
513 return unknownEnum(
static_cast<int64_t
>( aKind ) );
516static int64_t canonicalAngle( int64_t aAngleTenths )
521static std::vector<CANONICAL_SEMANTIC_RECORD> normalizeBinaryModel(
const PADS_SCH_MODEL& aModel )
523 std::vector<CANONICAL_SEMANTIC_RECORD> out;
524 auto add = [&]( CANONICAL_KIND aKind,
int aSheet, std::string aKey = {} ) -> CANONICAL_SEMANTIC_RECORD&
526 out.push_back( { aSheet, aKind, std::move( aKey ) } );
529 auto addOwned = [&]( CANONICAL_KIND aKind,
const SHEET_REFERENCE& aSheet,
530 std::string aKey = {} ) -> CANONICAL_SEMANTIC_RECORD&
532 auto sheet = std::ranges::find_if( aModel.
sheets,
535 return aCandidate.id == aSheet.id;
538 add( aKind, sheet == aModel.
sheets.end() ? -1 :
static_cast<int>( sheet->
index ), std::move( aKey ) );
539 record.properties[
"owner_sheet"] = sheet == aModel.
sheets.end()
540 ? unknownEnum( aSheet.id.Value() )
544 auto graphic = [&](
const MODEL_GRAPHIC& aGraphic,
int aSheet )
546 auto& r = add( CANONICAL_KIND::GRAPHIC, aSheet );
547 r.properties[
"type"] = canonicalGraphicType( aGraphic.
kind );
548 r.properties[
"line_style"] = canonicalLineStyle( aGraphic.
lineStyle );
549 r.properties[
"stroke_half_mils"] = { aGraphic.
strokeWidth };
550 r.properties[
"fill"] = canonicalFill( aGraphic.
fill );
551 r.properties[
"text"] = { aGraphic.
text.
text.ToStdString() };
559 r.properties[
"arc_center_x_half_mils"] = { aGraphic.
arcCenter.
x };
560 r.properties[
"arc_center_y_half_mils"] = { aGraphic.
arcCenter.
y };
561 r.properties[
"arc_bounds_x1_half_mils"] = { aGraphic.
arcBoundsStart.
x };
562 r.properties[
"arc_bounds_y1_half_mils"] = { aGraphic.
arcBoundsStart.
y };
563 r.properties[
"arc_bounds_x2_half_mils"] = { aGraphic.
arcBoundsEnd.
x };
564 r.properties[
"arc_bounds_y2_half_mils"] = { aGraphic.
arcBoundsEnd.
y };
565 r.properties[
"arc_sweep_tenths"] = { int64_t( aGraphic.
arcSweepAngle ) };
566 r.properties[
"arc_clockwise"] = { aGraphic.
arcClockwise };
570 r.geometry.points.push_back( point( p ) );
571 r.geometry.angleTenths = canonicalAngle( aGraphic.
angle );
572 addSourceProperties( r, aGraphic.
properties );
574 auto field = [&](
const MODEL_FIELD& aField,
int aSheet )
576 auto& r = add( CANONICAL_KIND::FIELD, aSheet, aField.
name.
text.ToStdString() );
577 r.properties[
"value"] = { aField.
value.
text.ToStdString() };
578 r.properties[
"visible"] = { aField.
visible };
580 r.geometry.points.push_back( point( aField.
position ) );
581 r.geometry.angleTenths = canonicalAngle( aField.
angle );
584 auto& settings = add( CANONICAL_KIND::SETTINGS, -1 );
592 auto& r = add( CANONICAL_KIND::SHEET,
static_cast<int>( s.
index ), s.
name.
text.ToStdString() );
593 r.properties[
"title"] = { s.
title.
text.ToStdString() };
596 r.geometry.points.push_back( point( s.
pageSize ) );
598 for(
const auto& g : s.
border )
599 graphic( g,
static_cast<int>( s.
index ) );
601 field( f,
static_cast<int>( s.
index ) );
605 auto& definition = add( CANONICAL_KIND::DEFINITION, d.source.sheet, d.name.text.ToStdString() );
606 addSourceProperties( definition, d.properties );
607 for(
const auto& g : d.graphics )
608 graphic( g, d.source.sheet );
609 for(
const auto& p : d.pins )
611 auto& r = add( CANONICAL_KIND::PIN, d.source.sheet, p.number.text.ToStdString() );
612 r.properties[
"name"] = { p.name.text.ToStdString() };
613 r.properties[
"type"] = canonicalPinType( p.electricalType );
614 r.properties[
"style"] = canonicalPinStyle( p.graphicStyle );
615 r.properties[
"length_half_mils"] = { p.length };
616 r.properties[
"font"] = { p.presentation.font.text.ToStdString() };
617 r.properties[
"visible"] = { p.presentation.visible };
618 r.geometry.points.push_back( point( p.position ) );
619 r.geometry.angleTenths = canonicalAngle( p.angle );
620 addSourceProperties( r, p.properties );
622 for(
const auto& f : d.fields )
623 field( f, d.source.sheet );
627 auto& part = add( CANONICAL_KIND::PART_TYPE, p.
source.
sheet, p.name.text.ToStdString() );
628 addSourceProperties( part, p.properties );
629 for(
const auto& g : p.gates )
631 auto& r = add( CANONICAL_KIND::GATE, g.source.sheet, std::to_string( g.unit ) );
632 auto definition = std::ranges::find_if( aModel.
definitions,
635 return aDefinition.id == g.definition.id;
637 r.properties[
"definition"] = definition == aModel.
definitions.end()
638 ? unknownEnum( g.definition.id.Value() )
640 addSourceProperties( r, g.properties );
641 for(
size_t i = 0; i < g.pins.size(); ++i )
643 auto& m = add( CANONICAL_KIND::GATE_PIN_MAPPING, g.source.sheet,
644 std::to_string( g.unit ) +
":" + std::to_string( i ) );
649 auto resolved = std::ranges::find_if( definition->pins,
652 return aPin.id == g.pins[i].id;
655 if( resolved != definition->pins.end() )
661 m.properties[
"pin_number"] = {
pin->number.text.ToStdString() };
662 m.properties[
"pin_name"] = {
pin->name.text.ToStdString() };
666 m.properties[
"pin_number"] = unknownEnum( g.pins[i].id.Value() );
667 m.properties[
"pin_name"] = unknownEnum( g.pins[i].id.Value() );
671 for(
const auto& f : p.fields )
676 auto& r = addOwned( CANONICAL_KIND::PLACEMENT, p.sheet, p.reference.text.ToStdString() );
677 r.properties[
"unit"] = { int64_t( p.unit ) };
678 r.properties[
"mirrored"] = { p.mirrored };
679 r.geometry.points.push_back( point( p.position ) );
680 r.geometry.angleTenths = canonicalAngle( p.angle );
681 addSourceProperties( r, p.properties );
682 int ownerSheet = r.sheet;
683 for(
const auto& f : p.fields )
684 field( f, ownerSheet );
686 for(
const auto& n : aModel.
nets )
688 auto& net = addOwned( CANONICAL_KIND::NET, n.sheet, n.name.text.ToStdString() );
689 addSourceProperties( net, n.properties );
690 for(
const auto& c : n.connections )
692 auto& r = addOwned( CANONICAL_KIND::CONNECTION, n.sheet );
693 r.properties[
"endpoint_count"] = { int64_t( c.endpoints.size() ) };
694 for(
size_t i = 0; i < c.endpoints.size(); ++i )
696 const auto prefix =
"endpoint_" + std::to_string( i ) +
"_";
697 r.properties[prefix +
"kind"] = canonicalEndpointKind( c.endpoints[i].kind );
698 addSourceProperties( r, c.endpoints[i].properties, prefix );
700 if( c.endpoints[i].placement )
702 auto placement = std::ranges::find_if( aModel.
placements,
705 return aPlacement.id == c.endpoints[i].placement->id;
707 r.properties[prefix +
"placement"] =
709 ? unknownEnum( c.endpoints[i].placement->id.Value() )
713 if( c.endpoints[i].pin )
715 auto definition = std::ranges::find_if( aModel.
definitions,
718 return std::ranges::any_of(
720 [&]( const MODEL_PIN_DEFINITION& aPin )
722 return aPin.id == c.endpoints[i].pin->id;
725 r.properties[prefix +
"pin_number"] =
727 ? unknownEnum( c.endpoints[i].pin->id.Value() )
728 : CANONICAL_PROPERTY{
std::ranges::find_if( definition->pins,
732 == c.endpoints[i].pin->id;
734 ->number.text.ToStdString() };
737 for(
const auto& p : c.vertices )
738 r.geometry.points.push_back( point( p ) );
739 addSourceProperties( r, c.properties );
742 for(
const auto& b : aModel.
buses )
744 auto netName = [&](
NET_ID aId ) -> CANONICAL_PROPERTY
746 auto net = std::ranges::find_if( aModel.
nets,
749 return aNet.id == aId;
752 if( net == aModel.
nets.end() )
753 return unknownEnum( aId.Value() );
755 return { net->name.text.ToStdString() };
757 auto& r = addOwned( CANONICAL_KIND::BUS, b.sheet, b.name.text.ToStdString() );
758 std::vector<std::string> aliases;
759 std::vector<std::string> members;
762 aliases.push_back( alias.
text.ToStdString() );
765 members.push_back( std::get<std::string>( netName( member.
id ).value ) );
767 r.properties[
"aliases"] = { aliases };
768 r.properties[
"member_nets"] = { members };
769 for(
const auto& p : b.vertices )
770 r.geometry.points.push_back( point( p ) );
771 addSourceProperties( r, b.properties );
772 for(
size_t i = 0; i < b.entries.size(); ++i )
774 auto& e = addOwned( CANONICAL_KIND::BUS_ENTRY, b.sheet, std::to_string( i ) );
775 e.properties[
"member_index"] = { int64_t( i ) };
776 e.properties[
"member_net"] = netName( b.entries[i].memberNet.id );
777 e.geometry.points.push_back( point( b.entries[i].position ) );
778 addSourceProperties( e, b.entries[i].properties );
780 for(
const auto& a : b.aliases )
781 addOwned( CANONICAL_KIND::BUS_ALIAS, b.sheet, a.text.ToStdString() );
782 for(
size_t i = 0; i < b.memberNets.size(); ++i )
784 auto& member = addOwned( CANONICAL_KIND::BUS_MEMBER, b.sheet, std::to_string( i ) );
785 member.properties[
"member_net"] = netName( b.memberNets[i].id );
788 for(
const auto& l : aModel.
labels )
790 auto& r = addOwned( CANONICAL_KIND::LABEL, l.sheet, l.text.text.ToStdString() );
791 r.properties[
"kind"] = canonicalLabelKind( l.kind );
792 std::vector<std::string> linkedSheets;
798 if( sheet != aModel.
sheets.end() )
799 linkedSheets.push_back( std::to_string( sheet->
index ) );
802 std::ranges::sort( linkedSheets );
803 r.properties[
"linked_sheets"] = { linkedSheets };
804 r.geometry.points.push_back( point( l.position ) );
805 r.geometry.angleTenths = canonicalAngle( l.angle );
806 addSourceProperties( r, l.properties );
810 auto& r = addOwned( CANONICAL_KIND::JUNCTION, j.sheet );
811 r.geometry.points.push_back( point( j.position ) );
812 addSourceProperties( r, j.properties );
814 for(
const auto& t : aModel.
texts )
816 auto& r = addOwned( CANONICAL_KIND::TEXT, t.sheet, t.text.text.ToStdString() );
817 r.properties[
"visible"] = { t.presentation.visible };
818 r.properties[
"font"] = { t.presentation.font.text.ToStdString() };
819 r.properties[
"bold"] = { t.presentation.bold };
820 r.properties[
"italic"] = { t.presentation.italic };
821 r.properties[
"underline"] = { t.presentation.underline };
822 r.properties[
"horizontal_justification"] = canonicalJustification( t.presentation.horizontalJustification );
823 r.properties[
"vertical_justification"] = canonicalVerticalJustification( t.presentation.verticalJustification );
824 r.geometry.points.push_back( point( t.position ) );
825 r.geometry.angleTenths = canonicalAngle( t.angle );
826 addSourceProperties( r, t.properties );
830 auto& record = addOwned( CANONICAL_KIND::GRAPHIC, pageGraphic.
sheet );
831 int sheet = record.sheet;
832 auto owner = record.
properties.at(
"owner_sheet" );
834 graphic( pageGraphic.
graphic, sheet );
835 out.back().properties[
"owner_sheet"] = owner;
839 auto& record = addOwned( CANONICAL_KIND::GRAPHIC, worksheet.
sheet );
840 int sheet = record.sheet;
841 auto owner = record.properties.at(
"owner_sheet" );
846 graphic( worksheetGraphic, sheet );
847 out.back().properties[
"owner_sheet"] = owner;
855 std::vector<CANONICAL_SEMANTIC_RECORD> out;
856 auto add = [&]( CANONICAL_KIND k,
int s, std::string key = {} ) -> CANONICAL_SEMANTIC_RECORD&
858 out.push_back( { s, k, std::move( key ) } );
862 auto addOwned = [&]( CANONICAL_KIND aKind,
int aSheet, std::string aKey = {} ) -> CANONICAL_SEMANTIC_RECORD&
867 return aCandidate.sheet_num - 1 == aSheet;
869 auto& record = add( aKind, aSheet, std::move( aKey ) );
870 record.properties[
"owner_sheet"] =
873 : CANONICAL_PROPERTY{ sheet->sheet_name };
876 auto& settings = add( CANONICAL_KIND::SETTINGS, -1 );
877 settings.properties[
"coordinate_units_per_mil"] = { int64_t( 2 ) };
878 settings.properties[
"line_width_half_mils"] = { int64_t( std::llround( p.line_width * 2 ) ) };
879 settings.properties[
"bus_width_half_mils"] = { int64_t( p.bus_width * 2 ) };
880 settings.geometry.points.push_back( point(
PADS_SCH::POINT{ p.sheet_size.width, p.sheet_size.height } ) );
883 auto& r = add( CANONICAL_KIND::GRAPHIC, aSheet );
884 r.properties[
"type"] = canonicalGraphicType( aGraphic.
type );
885 r.properties[
"line_style"] = canonicalLineStyle( aGraphic.
line_style );
886 r.properties[
"stroke_half_mils"] = { int64_t( std::llround( aGraphic.
line_width * 2 ) ) };
887 r.properties[
"fill"] = canonicalFill( aGraphic.
filled );
888 r.properties[
"text"] = { std::string() };
889 r.properties[
"font"] = { std::string() };
890 r.properties[
"visible"] = {
true };
891 r.properties[
"height_half_mils"] = { int64_t( 0 ) };
892 r.properties[
"width_half_mils"] = { int64_t( 0 ) };
894 for(
const auto& q : aGraphic.
points )
895 r.geometry.points.push_back( point( q.coord ) );
897 auto arcPoint = std::ranges::find_if( aGraphic.
points,
900 return aPoint.arc.has_value();
903 if( arcPoint != aGraphic.
points.end() )
906 r.properties[
"arc_center_x_half_mils"] = { int64_t( std::llround( ( arc.
bbox_x1 + arc.
bbox_x2 ) ) ) };
907 r.properties[
"arc_center_y_half_mils"] = { int64_t( std::llround( ( arc.
bbox_y1 + arc.
bbox_y2 ) ) ) };
908 r.properties[
"arc_bounds_x1_half_mils"] = { int64_t( std::llround( arc.
bbox_x1 * 2 ) ) };
909 r.properties[
"arc_bounds_y1_half_mils"] = { int64_t( std::llround( arc.
bbox_y1 * 2 ) ) };
910 r.properties[
"arc_bounds_x2_half_mils"] = { int64_t( std::llround( arc.
bbox_x2 * 2 ) ) };
911 r.properties[
"arc_bounds_y2_half_mils"] = { int64_t( std::llround( arc.
bbox_y2 * 2 ) ) };
912 r.properties[
"arc_sweep_tenths"] = { int64_t( std::llround(
std::abs( arc.
bulge ) ) ) };
913 r.properties[
"arc_clockwise"] = { arc.
angle < 0 };
918 auto& r = add( CANONICAL_KIND::SHEET, s.sheet_num - 1, s.sheet_name );
919 r.properties[
"title"] = { std::string() };
920 r.properties[
"line_width_half_mils"] = { int64_t( std::llround( p.line_width * 2 ) ) };
921 r.properties[
"bus_width_half_mils"] = { int64_t( p.bus_width * 2 ) };
922 r.geometry.points.push_back( point(
PADS_SCH::POINT{ p.sheet_size.width, p.sheet_size.height } ) );
926 add( CANONICAL_KIND::DEFINITION, -1, d.name );
927 for(
const auto& g : d.graphics )
929 std::vector<PADS_SCH::SYMBOL_PIN> pins = d.pins;
931 for(
const auto& [partName, part] : aParser.
GetPartTypes() )
938 for(
size_t i = 0; i < pins.size(); ++i )
940 pins[i].number = gate.
pins[i].pin_id;
941 pins[i].name = gate.
pins[i].pin_name;
942 switch( gate.
pins[i].pin_type )
961 for(
const auto&
pin : pins )
963 auto& r = add( CANONICAL_KIND::PIN, -1,
pin.number );
964 r.properties[
"name"] = {
pin.name };
965 r.properties[
"type"] = canonicalPinType(
pin.type );
966 r.properties[
"style"] = canonicalPinStyle(
pin.inverted,
pin.clock );
967 r.properties[
"length_half_mils"] = { int64_t( std::llround(
pin.length * 2 ) ) };
968 r.geometry.points.push_back( point(
pin.position ) );
969 r.geometry.angleTenths = canonicalAngle( std::llround(
pin.rotation * 10 ) );
971 for(
const auto& t : d.texts )
973 auto& r = add( CANONICAL_KIND::GRAPHIC, -1 );
974 r.properties[
"type"] = {
"text" };
975 r.properties[
"line_style"] = {
"solid" };
976 r.properties[
"stroke_half_mils"] = { int64_t( 0 ) };
977 r.properties[
"fill"] = {
"none" };
978 r.properties[
"text"] = { t.content };
979 r.properties[
"visible"] = { t.visible };
980 r.properties[
"font"] = { t.font_name };
981 r.properties[
"height_half_mils"] = { int64_t( std::llround( t.size ) ) };
982 r.properties[
"width_half_mils"] = { int64_t( t.width_factor ) };
983 r.geometry.points.push_back( point( t.position ) );
984 r.geometry.angleTenths = canonicalAngle( std::llround( t.rotation * 10 ) );
989 add( CANONICAL_KIND::PART_TYPE, -1,
name );
990 for(
size_t i = 0; i < part.gates.size(); ++i )
992 auto& gate = add( CANONICAL_KIND::GATE, -1, std::to_string( i + 1 ) );
993 gate.properties[
"definition"] = { part.gates[i].decal_names.empty() ? std::string()
994 : part.gates[i].decal_names.front() };
995 for(
size_t j = 0; j < part.gates[i].pins.size(); ++j )
997 auto& r = add( CANONICAL_KIND::GATE_PIN_MAPPING, -1,
998 std::to_string( i + 1 ) +
":" + std::to_string( j ) );
999 r.properties[
"pin_number"] = { part.gates[i].pins[j].pin_id };
1000 r.properties[
"pin_name"] = { part.gates[i].pins[j].pin_name };
1006 auto& r = addOwned( CANONICAL_KIND::PLACEMENT, x.sheet_number - 1, x.reference );
1007 r.properties[
"unit"] = { int64_t( x.gate_number ) };
1008 r.properties[
"mirrored"] = { x.mirror_flags != 0 };
1009 r.geometry.points.push_back( point( x.position ) );
1010 r.geometry.angleTenths = canonicalAngle( std::llround( x.rotation * 10 ) );
1011 int ownerSheet = r.sheet;
1012 for(
const auto& f : x.attributes )
1014 auto& q = add( CANONICAL_KIND::FIELD, ownerSheet, f.name );
1015 q.properties[
"value"] = { f.value };
1016 q.properties[
"visible"] = { f.visible };
1017 q.properties[
"font"] = { f.font_name };
1018 q.geometry.points.push_back( point( f.position ) );
1019 q.geometry.angleTenths = canonicalAngle( std::llround( f.rotation * 10 ) );
1024 if( n.flags1 == 5 && n.flags2 == 3 )
1027 int netSheet = n.wires.empty() ? -1 : n.wires.front().sheet_number - 1;
1028 addOwned( CANONICAL_KIND::NET, netSheet, n.name );
1029 for(
const auto& c : n.wires )
1031 auto& r = addOwned( CANONICAL_KIND::CONNECTION, c.sheet_number - 1 );
1032 r.properties[
"endpoint_count"] = { int64_t( 2 ) };
1034 const std::array<std::string, 2> endpoints = { c.endpoint_a, c.endpoint_b };
1036 for(
size_t endpoint = 0; endpoint < endpoints.size(); ++endpoint )
1038 const std::string prefix =
"endpoint_" + std::to_string( endpoint ) +
"_";
1039 const std::string& token = endpoints[endpoint];
1041 if( token.starts_with(
"@@@" ) )
1047 const size_t separator = token.rfind(
'.' );
1049 r.properties[prefix +
"placement"] = { token.substr( 0, separator ) };
1050 r.properties[prefix +
"pin_number"] = { token.substr( separator + 1 ) };
1054 for(
const auto& q : c.vertices )
1055 r.geometry.points.push_back( point( q ) );
1058 for(
const auto& b : aParser.
GetBuses() )
1060 auto& r = addOwned( CANONICAL_KIND::BUS, b.sheet_number - 1, b.name );
1061 r.properties[
"aliases"] = { b.aliases };
1062 r.properties[
"member_nets"] = { b.member_nets };
1063 for(
const auto& q : b.path )
1064 r.geometry.points.push_back( point( q ) );
1065 for(
size_t i = 0; i < b.entries.size(); ++i )
1067 auto& e = addOwned( CANONICAL_KIND::BUS_ENTRY, b.sheet_number - 1, std::to_string( i ) );
1068 e.properties[
"member_index"] = { int64_t( i ) };
1069 e.properties[
"member_net"] = { b.entries[i].member_net };
1070 e.geometry.points.push_back( point( b.entries[i].position ) );
1072 for(
const auto& a : b.aliases )
1073 addOwned( CANONICAL_KIND::BUS_ALIAS, b.sheet_number - 1, a );
1074 for(
size_t i = 0; i < b.member_nets.size(); ++i )
1076 auto& member = addOwned( CANONICAL_KIND::BUS_MEMBER, b.sheet_number - 1, std::to_string( i ) );
1077 member.properties[
"member_net"] = { b.member_nets[i] };
1079 if( std::ranges::none_of( aParser.
GetSignals(),
1082 return aSignal.name == b.member_nets[i];
1085 addOwned( CANONICAL_KIND::NET, b.sheet_number - 1, b.member_nets[i] );
1091 if( l.symbol_lib.starts_with(
"@@@B" ) )
1093 auto& r = addOwned( CANONICAL_KIND::LABEL, l.source_sheet - 1, l.signal_name );
1094 r.properties[
"kind"] = { l.symbol_lib ==
"@TERM" ?
"local"
1095 : l.symbol_lib.starts_with(
"$OSR" ) ?
"global"
1096 : l.symbol_lib.starts_with(
"$GND" ) ?
"ground"
1097 : l.symbol_lib.starts_with(
"$PWR" ) ?
"power"
1099 std::vector<std::string> linkedSheets;
1101 if( l.symbol_lib.starts_with(
"$OSR" ) )
1105 if( peer.signal_name != l.signal_name || peer.source_sheet == l.source_sheet
1106 || !peer.symbol_lib.starts_with(
"$OSR" ) )
1114 return aHeader.sheet_num == peer.source_sheet;
1118 linkedSheets.push_back( std::to_string( sheet->sheet_num - 1 ) );
1122 std::ranges::sort( linkedSheets );
1123 r.properties[
"linked_sheets"] = { linkedSheets };
1124 r.geometry.points.push_back( point( l.position ) );
1125 r.geometry.angleTenths = canonicalAngle( l.rotation * 10 );
1128 addOwned( CANONICAL_KIND::JUNCTION, j.sheet_number - 1 ).geometry.points.push_back( point( j.position ) );
1131 auto& r = addOwned( CANONICAL_KIND::TEXT, t.sheet_number - 1, t.content );
1132 r.properties[
"visible"] = {
true };
1133 r.properties[
"bold"] = {
false };
1134 r.properties[
"italic"] = {
false };
1135 r.properties[
"underline"] = {
false };
1136 r.geometry.points.push_back( point( t.position ) );
1137 r.geometry.angleTenths = canonicalAngle( t.rotation * 10 );
1141 for(
const auto& g : lines.primitives )
1143 auto& owner = addOwned( CANONICAL_KIND::GRAPHIC, lines.sheet_number - 1 );
1144 auto property = owner.properties.at(
"owner_sheet" );
1146 addGraphic( g, lines.sheet_number - 1 );
1147 out.back().properties[
"owner_sheet"] = property;
1148 out.back().properties[
"page_graphic_group"] = { lines.name };
1149 out.back().properties[
"type"] = canonicalGraphicType( g );
1151 for( CANONICAL_POINT& graphicPoint : out.back().geometry.points )
1153 graphicPoint.xHalfMils += std::llround( lines.origin.x * 2 );
1154 graphicPoint.yHalfMils += std::llround( lines.origin.y * 2 );
1157 for(
const std::string&
name :
1158 {
"arc_center_x_half_mils",
"arc_bounds_x1_half_mils",
"arc_bounds_x2_half_mils" } )
1160 auto property = out.back().properties.find(
name );
1162 if( property != out.back().properties.end() )
1163 std::get<int64_t>( property->second.value ) += std::llround( lines.origin.x * 2 );
1166 for(
const std::string&
name :
1167 {
"arc_center_y_half_mils",
"arc_bounds_y1_half_mils",
"arc_bounds_y2_half_mils" } )
1169 auto property = out.back().properties.find(
name );
1171 if( property != out.back().properties.end() )
1172 std::get<int64_t>( property->second.value ) += std::llround( lines.origin.y * 2 );
1179 if( out.back().geometry.points.size() == 2 )
1181 const CANONICAL_POINT first = out.back().geometry.points.front();
1182 const CANONICAL_POINT last = out.back().geometry.points.back();
1183 out.back().geometry.points = { { last.xHalfMils, last.yHalfMils },
1184 { first.xHalfMils, last.yHalfMils },
1185 { first.xHalfMils, first.yHalfMils },
1186 { last.xHalfMils, first.yHalfMils },
1187 { last.xHalfMils, last.yHalfMils } };
1191 out.back().geometry.angleTenths = 0;
1194 for(
const auto& t : lines.texts )
1196 auto& r = addOwned( CANONICAL_KIND::GRAPHIC, lines.sheet_number - 1 );
1199 r.properties[
"stroke_half_mils"] = { int64_t( 0 ) };
1201 r.properties[
"text"] = { t.content };
1202 r.properties[
"page_graphic_group"] = { lines.name };
1203 r.properties[
"visible"] = {
true };
1204 r.properties[
"height_half_mils"] = { int64_t( t.height ) };
1205 r.properties[
"width_half_mils"] = { int64_t( t.width_factor ) };
1206 r.properties[
"font"] = { t.font_name };
1207 r.geometry.points.push_back( point( t.position ) );
1208 r.geometry.points.back().xHalfMils += std::llround( lines.origin.x * 2 );
1209 r.geometry.points.back().yHalfMils += std::llround( lines.origin.y * 2 );
1210 r.geometry.angleTenths = canonicalAngle( t.rotation * 10 );
1215 auto& r = addOwned( CANONICAL_KIND::LABEL, -1, label.net_name );
1216 r.properties[
"kind"] = {
"local" };
1217 r.properties[
"visible"] = {
true };
1218 r.properties[
"font"] = { label.font_name };
1219 r.properties[
"horizontal_justification"] = canonicalHorizontalJustification( label.justification );
1220 r.properties[
"vertical_justification"] = canonicalVerticalJustification( label.justification );
1221 r.geometry.points.push_back( point(
PADS_SCH::POINT{ double( label.x_offset ), double( label.y_offset ) } ) );
1222 r.geometry.angleTenths = canonicalAngle( label.rotation * 10 );
1227static bool snapshotsMatch( std::vector<CANONICAL_SEMANTIC_RECORD> aExpected,
1228 std::vector<CANONICAL_SEMANTIC_RECORD> aActual,
1229 const SNAPSHOT_ALLOWLIST& aAllowedDifferences = {} )
1231 auto strip = [&](
auto& records )
1233 for(
auto& record : records )
1234 std::erase_if( record.properties,
1235 [&](
const auto& p )
1237 return aAllowedDifferences.contains( { p.first, p.second.disposition } );
1239 std::sort( records.begin(), records.end() );
1243 return aExpected == aActual;
1247static bool task10SourcePropertiesPresent(
const std::vector<CANONICAL_SEMANTIC_RECORD>& aRecords )
1250 [](
const CANONICAL_SEMANTIC_RECORD& aRecord,
const std::string& aName,
PROPERTY_DISPOSITION aDisposition )
1252 auto property = aRecord.properties.find( aName );
1253 return property != aRecord.properties.end() &&
property->second.disposition == aDisposition;
1256 for(
const CANONICAL_SEMANTIC_RECORD& record : aRecords )
1258 if( record.kind == CANONICAL_KIND::NET
1266 if( record.kind == CANONICAL_KIND::CONNECTION
1281 if( record.kind == CANONICAL_KIND::JUNCTION
1287 if( record.kind == CANONICAL_KIND::BUS
1307 std::vector<uint8_t> bytes = loadBinaryFixture(
"ole_images.sch" );
1310 BOOST_REQUIRE_EQUAL(
model.images.size(), 2 );
1315 BOOST_CHECK( (
model.images[0].extent == std::array<int32_t, 4>{ 30, 30, 350, 210 } ) );
1316 BOOST_CHECK( (
model.images[0].databaseBox == std::array<int32_t, 4>{ -15766, -10366, -13296, -11755 } ) );
1322 BOOST_REQUIRE_GE(
model.images[0].data.size(), 2 );
1327 BOOST_CHECK_GT(
model.images[0].source.absoluteOffset, 0 );
1328 BOOST_CHECK_GT(
model.images[0].source.length, 512 );
1334 BOOST_CHECK( (
model.images[1].extent == std::array<int32_t, 4>{ 30, 30, 170, 84 } ) );
1335 BOOST_CHECK( (
model.images[1].databaseBox == std::array<int32_t, 4>{ -15766, -10366, -14686, -10782 } ) );
1341 BOOST_REQUIRE_GE(
model.images[1].data.size(), 4 );
1349 BOOST_REQUIRE_EQUAL( sdb.
OleItems().size(), 2u );
1350 std::vector<uint8_t> zeroWidth = bytes;
1351 writeU32( zeroWidth, sdb.
OleItems()[0].boxOffset + 8,
static_cast<uint32_t
>( sdb.
OleItems()[0].left ) );
1353 BOOST_REQUIRE_EQUAL( zeroWidthModel.
images.size(), 2 );
1355 BOOST_CHECK( std::ranges::any_of( zeroWidthModel.
diagnostics,
1358 return aDiagnostic.source.objectClass
1359 == wxS(
"embedded OLE image database box" )
1360 && aDiagnostic.message.Contains( wxS(
"zero-size" ) );
1368 PADS_SCH_MODEL minimal = parser.
Parse( loadBinaryFixture(
"minimal_v13.sch" ), wxS(
"minimal_v13.sch" ) );
1383 BOOST_REQUIRE_EQUAL( minimal.
sheets.size(), 1 );
1387 BOOST_CHECK( !minimal.
sheets[0].parent );
1390 BOOST_REQUIRE_EQUAL( minimal.
sheets[0].titleBlockFields.size(), 14 );
1394 const std::array<wxString, 14> titleNames = { wxS(
"Drawn By" ), wxS(
"Checked By" ), wxS(
"QC By" ),
1395 wxS(
"Released By" ), wxS(
"Drawn Date" ), wxS(
"Checked Date" ),
1396 wxS(
"QC Date" ), wxS(
"Release Date" ), wxS(
"Company Name" ),
1397 wxS(
"Title" ), wxS(
"Code" ), wxS(
"Drawing Number" ),
1398 wxS(
"Revision" ), wxS(
"Scale" ) };
1399 const std::array<size_t, 14> titleOffsets = { 0x254, 0x264, 0x276, 0x283, 0x296, 0x2A8, 0x2BC,
1400 0x2CB, 0x2DF, 0x2F3, 0x300, 0x30C, 0x322, 0x332 };
1401 const std::array<size_t, 14> titleLengths = { 16, 18, 13, 19, 18, 20, 15, 20, 20, 13, 12, 22, 16, 13 };
1403 for(
size_t i = 0; i < titleNames.size(); ++i )
1406 const std::string expectedName = titleNames[i].ToStdString();
1410 expectedName.end() );
1411 BOOST_CHECK( field.
value.
raw.empty() );
1439 parser.
Parse( loadBinaryFixture(
"multisheet_connectivity.sch" ), wxS(
"multisheet_connectivity.sch" ) );
1440 BOOST_REQUIRE_EQUAL( multisheet.
sheets.size(), 2 );
1445 BOOST_CHECK_LT( multisheet.
sheets[0].source.absoluteOffset, multisheet.
sheets[1].source.absoluteOffset );
1446 BOOST_CHECK( !multisheet.
sheets[0].parent );
1447 BOOST_CHECK( !multisheet.
sheets[1].parent );
1465 return aField.name.text == wxS(
"Title" );
1468 BOOST_CHECK( sheet.
title == title->value );
1475 std::vector<uint8_t> bytes = loadBinaryFixture(
"minimal_v13.sch" );
1476 const size_t poolOffset = outerControllerOffset( bytes, 1 );
1477 const size_t poolBytes = readU32( bytes, 0x20 + 1 * 28 + 12 );
1478 const std::array<std::pair<std::string, std::string>, 5>
expected = {
1479 std::pair{
"Drawn By",
"Alice" }, std::pair{
"Checked By",
"Bob" }, std::pair{
"Title",
"Variable Title" },
1480 std::pair{
"Revision",
"C" }, std::pair{
"Scale",
"2:1" }
1482 std::vector<uint8_t> pool;
1483 std::array<size_t,
expected.size()> offsets;
1484 std::array<size_t,
expected.size()> lengths;
1486 for(
size_t i = 0; i <
expected.size(); ++i )
1488 offsets[i] = poolOffset + pool.size();
1489 const std::string slot =
"Field\n" +
expected[i].first +
'\x01' +
expected[i].second +
'\0';
1490 lengths[i] = slot.size();
1491 pool.insert( pool.end(), slot.begin(), slot.end() );
1494 const size_t firstNonFieldOffset = poolOffset + pool.size();
1495 const std::string firstNonField(
"Font Default\0", 13 );
1496 pool.insert( pool.end(), firstNonField.begin(), firstNonField.end() );
1497 BOOST_REQUIRE_LE( pool.size(), poolBytes );
1498 pool.resize( poolBytes, 0 );
1499 std::copy( pool.begin(), pool.end(), bytes.begin() + poolOffset );
1503 BOOST_REQUIRE_EQUAL(
model.sheets.size(), 1 );
1504 BOOST_REQUIRE_EQUAL(
model.sheets[0].titleBlockFields.size(),
expected.size() );
1506 for(
size_t i = 0; i <
expected.size(); ++i )
1536 bytes.begin() + firstNonFieldOffset + firstNonField.size(), firstNonField.begin(),
1537 firstNonField.end() );
1539 +
model.sheets[0].titleBlockFields.back().source.length,
1540 firstNonFieldOffset );
1549 BOOST_REQUIRE_EQUAL(
model.texts.size(), 1 );
1563 BOOST_CHECK( !
text.presentation.bold );
1564 BOOST_CHECK( !
text.presentation.italic );
1565 BOOST_CHECK( !
text.presentation.underline );
1566 BOOST_CHECK(
text.presentation.visible );
1567 BOOST_REQUIRE_EQUAL(
text.properties.size(), 1 );
1579 std::vector<uint8_t> cp1252Bytes = loadBinaryFixture(
"text_encoding.sch" );
1580 cp1252Bytes[
text.text.source.absoluteOffset +
text.text.source.length - 1] = 0xE9;
1582 BOOST_REQUIRE_EQUAL( cp1252.
texts.size(), 1 );
1592 std::vector<uint8_t> invalidOffset = loadBinaryFixture(
"text_encoding.sch" );
1593 writeU32( invalidOffset,
text.source.absoluteOffset + 8, std::numeric_limits<uint32_t>::max() );
1594 BOOST_CHECK_EXCEPTION( parser.
Parse( invalidOffset, wxS(
"invalid-offset.sch" ) ),
IO_ERROR,
1597 return aError.
What().Contains( wxString::Format( wxS(
"offset 0x%zX" ),
1598 text.source.absoluteOffset + 8 ) )
1599 && aError.
What().Contains( wxS(
"string offset leaves controller 2" ) );
1602 std::vector<uint8_t> invalidLength = loadBinaryFixture(
"text_encoding.sch" );
1603 writeU16( invalidLength,
text.source.absoluteOffset + 20, std::numeric_limits<uint16_t>::max() );
1604 BOOST_CHECK_EXCEPTION( parser.
Parse( invalidLength, wxS(
"invalid-length.sch" ) ),
IO_ERROR,
1607 return aError.
What().Contains( wxString::Format( wxS(
"offset 0x%zX" ),
1608 text.source.absoluteOffset + 20 ) )
1609 && aError.
What().Contains( wxS(
"string length leaves controller 2" ) );
1612 std::vector<uint8_t> missingNul = loadBinaryFixture(
"text_encoding.sch" );
1613 const size_t terminatorOffset =
text.text.source.absoluteOffset +
text.text.source.length;
1614 missingNul[terminatorOffset] =
'X';
1615 BOOST_CHECK_EXCEPTION( parser.
Parse( missingNul, wxS(
"missing-nul.sch" ) ),
IO_ERROR,
1618 return aError.
What().Contains(
1619 wxString::Format( wxS(
"offset 0x%zX" ), terminatorOffset ) )
1620 && aError.
What().Contains( wxS(
"not NUL terminated" ) )
1621 && aError.
What().Contains( wxS(
"controller 2" ) );
1626 std::vector<CANONICAL_SEMANTIC_RECORD>
expected = normalizeAsciiModel( ascii );
1627 std::vector<CANONICAL_SEMANTIC_RECORD>
actual = normalizeBinaryModel(
model );
1628 auto retainTask7Properties = []( std::vector<CANONICAL_SEMANTIC_RECORD>& aRecords )
1630 std::erase_if( aRecords,
1631 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
1633 return aRecord.kind != CANONICAL_KIND::SETTINGS && aRecord.kind != CANONICAL_KIND::SHEET
1634 && ( aRecord.kind != CANONICAL_KIND::TEXT
1635 || aRecord.stableKey !=
"ascii-text cafe=" );
1638 for( CANONICAL_SEMANTIC_RECORD& record : aRecords )
1640 if( record.kind != CANONICAL_KIND::TEXT )
1643 std::erase_if( record.properties,
1644 [](
const auto& aProperty )
1646 return aProperty.first !=
"owner_sheet" && aProperty.first !=
"visible"
1647 && aProperty.first !=
"bold" && aProperty.first !=
"italic"
1648 && aProperty.first !=
"underline";
1653 retainTask7Properties(
actual );
1656 std::vector<uint8_t> changedRelationship = loadBinaryFixture(
"text_encoding.sch" );
1657 writeU16( changedRelationship,
text.source.absoluteOffset + 28, 0xBEEF );
1659 BOOST_REQUIRE_EQUAL( changed.
texts.size(), 1 );
1668 struct JUSTIFICATION_CASE
1675 for(
const JUSTIFICATION_CASE&
expected :
1684 std::vector<uint8_t> changedJustification = loadBinaryFixture(
"text_encoding.sch" );
1685 writeU16( changedJustification,
text.source.absoluteOffset + 18,
expected.raw );
1686 PADS_SCH_MODEL justified = parser.
Parse( changedJustification, wxS(
"text_encoding.sch" ) );
1687 BOOST_REQUIRE_EQUAL( justified.
texts.size(), 1 );
1688 BOOST_CHECK( justified.
texts[0].presentation.horizontalJustification ==
expected.horizontal );
1689 BOOST_CHECK( justified.
texts[0].presentation.verticalJustification ==
expected.vertical );
1700 for(
const FONT_CASE&
expected : { FONT_CASE{ 0,
false,
false }, FONT_CASE{ 1,
false,
true },
1701 FONT_CASE{ 2,
true,
false }, FONT_CASE{ 3,
true,
true } } )
1703 std::vector<uint8_t> changedFont = loadBinaryFixture(
"text_encoding.sch" );
1704 writeU16( changedFont,
text.source.absoluteOffset, 0 );
1705 writeU32( changedFont, outerControllerOffset( changedFont, 19 ),
expected.style );
1707 BOOST_REQUIRE_EQUAL( styled.
texts.size(), 1 );
1713 std::vector<uint8_t> hiddenText = loadBinaryFixture(
"text_encoding.sch" );
1714 hiddenText[
text.source.absoluteOffset + 31] = 1;
1716 BOOST_REQUIRE_EQUAL( hidden.
texts.size(), 1 );
1717 BOOST_CHECK( !hidden.
texts[0].presentation.visible );
1727 BOOST_REQUIRE_EQUAL(
model.texts.size(), 26 );
1729 auto textByName = [&](
const wxString& aName ) ->
const MODEL_TEXT&
1731 auto found = std::ranges::find_if(
model.texts,
1734 return aText.text.text == aName;
1749 for(
size_t ii = 0; ii < horizontal.size(); ++ii )
1756 const MODEL_TEXT&
text = textByName( wxString::Format( wxS(
"JUST_%02zu" ), ii ) );
1757 BOOST_CHECK(
text.presentation.horizontalJustification == horizontal[ii] );
1758 BOOST_CHECK(
text.presentation.verticalJustification == vertical );
1761 for(
const auto& [
name, angle] : { std::pair{ wxS(
"ANGLE_000" ), 0 }, std::pair{ wxS(
"ANGLE_090" ), 900 },
1762 std::pair{ wxS(
"ANGLE_180" ), 1800 }, std::pair{ wxS(
"ANGLE_270" ), 2700 } } )
1767 BOOST_CHECK( !textByName( wxS(
"STYLE_REGULAR" ) ).presentation.bold );
1768 BOOST_CHECK( !textByName( wxS(
"STYLE_REGULAR" ) ).presentation.italic );
1769 BOOST_CHECK( textByName( wxS(
"STYLE_BOLD" ) ).presentation.bold );
1770 BOOST_CHECK( textByName( wxS(
"STYLE_ITALIC" ) ).presentation.italic );
1771 BOOST_CHECK( textByName( wxS(
"STYLE_BOLD_ITALIC" ) ).presentation.bold );
1772 BOOST_CHECK( !textByName( wxS(
"STYLE_BOLD_ITALIC" ) ).presentation.italic );
1774 BOOST_CHECK( textByName( wxS(
"VISIBLE_0" ) ).presentation.visible );
1775 BOOST_CHECK( !textByName( wxS(
"VISIBLE_1" ) ).presentation.visible );
1784 wxS(
"visibility_options.sch" ) );
1786 auto underlined = std::ranges::find_if(
model.texts,
1789 return aText.text.text == wxS(
"STYLE_UNDERLINE" );
1792 BOOST_CHECK( underlined->presentation.underline );
1793 BOOST_CHECK( !underlined->presentation.bold );
1794 BOOST_CHECK( !underlined->presentation.italic );
1797 BOOST_REQUIRE_EQUAL(
model.placements.size(), 32 );
1799 for( uint8_t flags = 0; flags < 32; ++flags )
1801 const wxString reference = wxString::Format( wxS(
"R%u" ), flags + 1 );
1802 auto placement = std::ranges::find(
model.placements, reference,
1805 return aPlacement.reference.text;
1807 BOOST_REQUIRE_MESSAGE( placement !=
model.placements.end(), reference );
1819 SOURCE_PROVENANCE source{ wxS(
"text_encoding.sch" ), 0x000D, wxS(
"free text" ), 2, 0, 0x6250, 2, 0 };
1820 std::vector<PARSER_DIAGNOSTIC> diagnostics;
1828 BOOST_REQUIRE_EQUAL( diagnostics.size(), 2 );
1830 BOOST_CHECK( diagnostics[0].message.Contains( wxS(
"code page 932" ) ) );
1831 BOOST_CHECK( diagnostics[1].message.Contains( wxS(
"invalid UTF-8" ) ) );
1838 std::vector<uint8_t> reordered = loadBinaryFixture(
"multisheet_connectivity.sch" );
1839 size_t sheetIndex = outerControllerOffset( reordered, 3 );
1840 writeU16( reordered, sheetIndex + 8, 2 );
1841 writeU16( reordered, sheetIndex + 48 + 8, 1 );
1843 BOOST_REQUIRE_EQUAL( reorderedModel.
sheets.size(), 2u );
1851 std::vector<uint8_t>
duplicate = loadBinaryFixture(
"multisheet_connectivity.sch" );
1852 sheetIndex = outerControllerOffset(
duplicate, 3 );
1853 writeU16(
duplicate, sheetIndex + 48 + 8, 1 );
1857 return aError.
What().Contains( wxS(
"duplicate sheet ID 1" ) )
1858 && aError.
What().Contains( wxS(
"controller 3" ) );
1861 std::vector<uint8_t> wrongClassBytes = loadMinimalV13();
1862 sheetIndex = outerControllerOffset( wrongClassBytes, 3 );
1863 writeU32( wrongClassBytes, sheetIndex,
static_cast<uint32_t
>( outerControllerOffset( wrongClassBytes, 5 ) ) );
1864 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongClassBytes, wxS(
"wrong-class.sch" ) ),
IO_ERROR,
1867 return aError.
What().Contains( wxS(
"references the wrong SDB object class" ) )
1868 && aError.
What().Contains( wxS(
"controller 3" ) );
1872 SOURCE_PROVENANCE refSource{ wxS(
"wrong-class.sch" ), 0x000D, wxS(
"free text" ), 1, 0, 0x100, 32, 0 };
1873 wrongClass.texts.push_back( { refSource, {
SHEET_ID( 99 ), refSource } } );
1874 BOOST_CHECK_EXCEPTION( wrongClass.ValidateOrThrow(),
IO_ERROR,
1877 return aError.What().Contains( wxS(
"unresolved text sheet reference" ) );
1885 return aError.What().Contains( wxS(
"cyclic sheet hierarchy" ) );
1889 longHierarchy.
version = 0x000D;
1890 constexpr size_t hierarchySize = 4096;
1891 longHierarchy.
sheets.reserve( hierarchySize );
1893 for(
size_t i = 0; i < hierarchySize; ++i )
1895 SOURCE_PROVENANCE source{ wxS(
"long-hierarchy.sch" ), 0x000D, wxS(
"sheet" ), 3, i, 0x100 + i * 48, 48,
1896 static_cast<int>( i ) };
1905 longHierarchy.
sheets.push_back( std::move( sheet ) );
1913 return aError.What().Contains( wxS(
"cyclic sheet hierarchy" ) )
1914 && aError.What().Contains( wxS(
"controller 3" ) );
1921 static_assert( !std::is_same_v<SHEET_ID, PLACEMENT_ID> );
1922 static_assert( !std::is_convertible_v<SHEET_ID, PLACEMENT_ID> );
1924 const std::vector<uint8_t> bytes = loadMinimalV13();
1930 BOOST_REQUIRE_EQUAL( first.
sheets.size(), 1 );
1934 BOOST_CHECK_GT( first.
sheets[0].source.length, 0 );
1942 BOOST_CHECK( first == second );
1964 return aError.What().Contains( wxS(
"unresolved" ) );
1972 return aError.What().Contains( wxS(
"cyclic" ) )
1973 && aError.What().Contains( wxS(
"offset" ) );
1976 SOURCE_PROVENANCE textSource{ wxS(
"strings.sch" ), 0x000D, wxS(
"text" ), 4, 7, 0x1234, 2, 0 };
1977 std::vector<PARSER_DIAGNOSTIC> diagnostics;
1983 BOOST_REQUIRE_EQUAL( diagnostics.size(), 3 );
1986 BOOST_CHECK( diagnostics[2].message.Contains( wxS(
"line style 17" ) ) );
1992 SOURCE_PROVENANCE source{ wxS(
"model.sch" ), 0x000D, wxS(
"connection" ), 8, 3, 0x200, 16, 0 };
1994 model.version = 0x000D;
1995 model.source = source;
1998 model.definitions[0].pins.push_back( {
PIN_ID( 2 ), source } );
2000 model.partTypes[0].gates.push_back(
2008 { {
PIN_ID( 2 ), source } } } );
2010 model.nets[0].connections.push_back( { source } );
2014 BOOST_CHECK_NO_THROW(
model.ValidateOrThrow() );
2036 SOURCE_PROVENANCE topologySource{ wxS(
"model.sch" ), 0x000D, wxS(
"endpoint" ), 9, 4, 0x280, 16, 1 };
2038 crossSheet.
sheets.push_back( {
SHEET_ID( 1 ), 1, topologySource } );
2039 crossSheet.
nets[0].sheet = {
SHEET_ID( 1 ), topologySource };
2040 crossSheet.
nets[0].connections[0].endpoints[0].source = topologySource;
2041 const wxString endpointError =
2042 FormatParserError( topologySource, wxS(
"connection endpoint placement sheet does not match net sheet" ) );
2046 return aError.What().Contains( endpointError );
2050 busModel.
nets[0].connections.clear();
2052 busModel.
buses[0].memberNets.push_back( {
NET_ID( 6 ), source } );
2053 busModel.
buses[0].entries.push_back( { topologySource, {}, {
NET_ID( 6 ), topologySource } } );
2057 missingBusMember.
buses[0].memberNets.clear();
2058 const wxString membershipError =
2059 FormatParserError( topologySource, wxS(
"bus-entry net is absent from bus member nets" ) );
2063 return aError.What().Contains( membershipError );
2067 crossSheetBus.
sheets.push_back( {
SHEET_ID( 1 ), 1, topologySource } );
2068 crossSheetBus.
buses[0].sheet = {
SHEET_ID( 1 ), topologySource };
2069 const wxString busSheetError =
FormatParserError( source, wxS(
"bus member-net sheet does not match bus sheet" ) );
2073 return aError.What().Contains( busSheetError );
2080 SOURCE_PROVENANCE source{ wxS(
"graphics.sch" ), 0x000D, wxS(
"page graphic" ), 5, 6, 0x420, 24, 0 };
2082 model.source = source;
2085 BOOST_CHECK_NO_THROW(
model.ValidateOrThrow() );
2088 auto records = normalizeBinaryModel(
model );
2089 auto record = std::ranges::find_if( records,
2090 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
2092 return aRecord.kind == CANONICAL_KIND::GRAPHIC;
2096 const wxString error =
FormatParserError( source, wxS(
"unresolved page-graphic sheet reference" ) );
2100 return aError.What().Contains( error );
2107 SOURCE_PROVENANCE firstSource{ wxS(
"ids.sch" ), 0x000D, wxS(
"sheet" ), 3, 1, 0x100, 48, 0 };
2108 SOURCE_PROVENANCE secondSource{ wxS(
"ids.sch" ), 0x000D, wxS(
"sheet" ), 3, 2, 0x200, 48, 1 };
2110 duplicate.source = { wxS(
"ids.sch" ), 0x000D, wxS(
"model" ), -1, 0, 0, 0x300, -1 };
2114 const wxString duplicateDetail =
2115 wxS(
"duplicate sheet ID 7; first at v0x000D sheet controller 3 record 1 sheet 0 offset 0x100" );
2116 const wxString duplicateError =
FormatParserError( secondSource, duplicateDetail );
2120 return aError.What().Contains( duplicateError );
2126 const wxString invalidError =
FormatParserError( secondSource, wxS(
"invalid sheet ID" ) );
2130 return aError.What().Contains( invalidError );
2137 SOURCE_PROVENANCE source{ wxS(
"encoding.sch" ), 0x000D, wxS(
"text" ), 4, 8, 0x300, 4, 0 };
2138 std::vector<PARSER_DIAGNOSTIC> diagnostics;
2145 BOOST_CHECK( utf8.
source == source );
2149 BOOST_REQUIRE_EQUAL( cp1252.
text.length(), 3 );
2160 BOOST_REQUIRE_EQUAL(
unknown.text.length(), 2 );
2169 BOOST_REQUIRE_EQUAL( invalidUtf8.
text.length(), 4 );
2175 BOOST_REQUIRE_EQUAL( diagnostics.size(), 2 );
2177 BOOST_CHECK( diagnostics[0].message.Contains( wxS(
"99999" ) ) );
2179 BOOST_CHECK( diagnostics[1].message.Contains( wxS(
"UTF-8" ) ) );
2183 BOOST_REQUIRE_EQUAL( diagnostics.size(), 3 );
2184 BOOST_CHECK( diagnostics[2].message.Contains( wxS(
"932" ) ) );
2190 SOURCE_PROVENANCE source{ wxS(
"snapshot.sch" ), 0x000D, wxS(
"sheet" ), 3, 0, 0x250, 48, 0 };
2200 BOOST_CHECK_NE( binary.
sheets[0].id.Value(), binary.
sheets[1].id.Value() );
2201 BOOST_CHECK( normalizeBinaryModel( binary ) == normalizeBinaryModel( binary ) );
2219 std::vector<CANONICAL_SEMANTIC_RECORD>
expected = normalizeBinaryModel( binary );
2223 for( CANONICAL_SEMANTIC_RECORD& item :
actual )
2225 if( item.properties.contains(
"font" ) )
2226 item.properties.at(
"font" ).value = std::string(
"Arial" );
2228 if( item.properties.contains(
"color" ) )
2229 item.properties.at(
"color" ).value = std::string(
"red" );
2234 BOOST_CHECK( snapshotsMatch(
2239 BOOST_CHECK( !snapshotsMatch(
2247 SOURCE_PROVENANCE source{ wxS(
"snapshot.sch" ), 0x000D, wxS(
"synthetic" ), 1, 0, 0x250, 16, 0 };
2248 auto string = [&](
const wxString& aText )
2257 model.settings.source = source;
2258 model.sheets.push_back( {
SHEET_ID( 1 ), 0, source, string( wxS(
"Main" ) ) } );
2262 definition.
source = source;
2263 definition.
name = string( wxS(
"SYM" ) );
2264 definition.
graphics.push_back( { source } );
2265 definition.
pins.push_back( {
PIN_ID( 3 ), source, string( wxS(
"1" ) ) } );
2266 definition.
fields.push_back( {
FIELD_ID( 10 ), source, string( wxS(
"Value" ) ), string( wxS(
"R" ) ) } );
2267 model.definitions.push_back( definition );
2271 partType.
source = source;
2272 partType.
name = string( wxS(
"RES" ) );
2273 partType.
gates.push_back(
2275 partType.
fields.push_back( {
FIELD_ID( 11 ), source, string( wxS(
"Tolerance" ) ), string( wxS(
"1%" ) ) } );
2276 model.partTypes.push_back( partType );
2280 placement.
source = source;
2281 placement.
reference = string( wxS(
"R1" ) );
2282 placement.
fields.push_back( {
FIELD_ID( 12 ), source, string( wxS(
"Value" ) ), string( wxS(
"10k" ) ) } );
2283 model.placements.push_back( placement );
2288 net.
name = string( wxS(
"N" ) );
2290 model.nets.push_back( net );
2295 bus.
name = string( wxS(
"D[0..0]" ) );
2296 bus.
entries.push_back( { source, {}, {
NET_ID( 7 ), source } } );
2297 bus.
aliases.push_back(
string( wxS(
"DATA" ) ) );
2299 model.buses.push_back( bus );
2301 model.junctions.push_back( { source } );
2302 model.texts.push_back( { source, {}, string( wxS(
"note" ) ) } );
2303 model.graphics.push_back( { source, {
SHEET_ID( 1 ), source }, { source } } );
2305 std::vector<CANONICAL_SEMANTIC_RECORD> binaryRecords = normalizeBinaryModel(
model );
2306 std::set<CANONICAL_KIND> binaryKinds;
2308 for(
const CANONICAL_SEMANTIC_RECORD& item : binaryRecords )
2309 binaryKinds.insert( item.kind );
2311 const std::set<CANONICAL_KIND> expectedKinds{
2312 CANONICAL_KIND::SETTINGS, CANONICAL_KIND::SHEET, CANONICAL_KIND::DEFINITION,
2313 CANONICAL_KIND::GRAPHIC, CANONICAL_KIND::PIN, CANONICAL_KIND::FIELD,
2314 CANONICAL_KIND::PART_TYPE, CANONICAL_KIND::GATE, CANONICAL_KIND::GATE_PIN_MAPPING,
2315 CANONICAL_KIND::PLACEMENT, CANONICAL_KIND::NET, CANONICAL_KIND::CONNECTION,
2316 CANONICAL_KIND::BUS, CANONICAL_KIND::BUS_ENTRY, CANONICAL_KIND::BUS_ALIAS,
2317 CANONICAL_KIND::BUS_MEMBER, CANONICAL_KIND::LABEL, CANONICAL_KIND::JUNCTION,
2318 CANONICAL_KIND::TEXT
2320 BOOST_CHECK( binaryKinds == expectedKinds );
2325 std::vector<CANONICAL_SEMANTIC_RECORD> asciiRecords = normalizeAsciiModel( asciiParser );
2326 BOOST_CHECK( std::ranges::any_of( asciiRecords,
2327 [](
const CANONICAL_SEMANTIC_RECORD& aItem )
2329 return aItem.kind == CANONICAL_KIND::SETTINGS;
2331 BOOST_CHECK( std::ranges::any_of( asciiRecords,
2332 [](
const CANONICAL_SEMANTIC_RECORD& aItem )
2334 return aItem.kind == CANONICAL_KIND::SHEET;
2338 std::vector<CANONICAL_SEMANTIC_RECORD> minimalBinary =
2339 normalizeBinaryModel( binaryParser.Parse( loadMinimalV13(), wxS(
"minimal_v13.sch" ) ) );
2340 auto countKind = [](
const std::vector<CANONICAL_SEMANTIC_RECORD>& aItems, CANONICAL_KIND aKind )
2342 return std::ranges::count_if( aItems,
2343 [&](
const CANONICAL_SEMANTIC_RECORD& aItem )
2345 return aItem.kind == aKind;
2349 countKind( asciiRecords, CANONICAL_KIND::SETTINGS ) );
2351 countKind( asciiRecords, CANONICAL_KIND::SHEET ) );
2361 SOURCE_PROVENANCE source{ wxS(
"buses.sch" ), 0x000D, wxS(
"synthetic" ), 1, 0, 0, 0, 0 };
2362 auto string = [&](
const wxString& aText )
2378 sheet.defaultLineWidth = 20;
2379 sheet.defaultBusWidth = 50;
2380 binary.
sheets.push_back( sheet );
2382 for(
int i = 0; i < 3; ++i )
2388 net.
name = string( wxString::Format( wxS(
"DATA%d" ), i ) );
2389 binary.
nets.push_back( net );
2396 bus.
name = string( wxS(
"DATA[0..2]" ) );
2397 bus.
vertices = { { 6000, 14000, source }, { 14000, 14000, source }, { 14000, 10000, source } };
2398 bus.
aliases.push_back(
string( wxS(
"DATA[0..2]" ) ) );
2400 for(
int i = 0; i < 3; ++i )
2402 bus.
entries.push_back( { source, { 8000 + i * 2000, 14000, source }, {
NET_ID( i + 1 ), source } } );
2406 binary.
buses.push_back( bus );
2407 BOOST_CHECK( snapshotsMatch( normalizeBinaryModel( binary ), normalizeAsciiModel( asciiParser ) ) );
2409 std::vector<CANONICAL_SEMANTIC_RECORD> records = normalizeBinaryModel( binary );
2410 auto entry = std::ranges::find_if( records,
2411 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
2413 return aRecord.kind == CANONICAL_KIND::BUS_ENTRY;
2416 BOOST_CHECK( entry->properties.at(
"member_net" ).value == CANONICAL_VALUE( std::string(
"DATA0" ) ) );
2417 auto busRecord = std::ranges::find_if( records,
2418 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
2420 return aRecord.kind == CANONICAL_KIND::BUS;
2423 BOOST_CHECK( busRecord->properties.at(
"member_nets" ).value
2424 == CANONICAL_VALUE( std::vector<std::string>{
"DATA0",
"DATA1",
"DATA2" } ) );
2427 wrongMapping.
buses[0].entries[0].memberNet.id =
NET_ID( 2 );
2428 BOOST_CHECK( !snapshotsMatch( normalizeBinaryModel( wrongMapping ), normalizeAsciiModel( asciiParser ) ) );
2431 missingMember.
buses[0].memberNets.pop_back();
2432 BOOST_CHECK( !snapshotsMatch( normalizeBinaryModel( missingMember ), normalizeAsciiModel( asciiParser ) ) );
2439 == CANONICAL_VALUE( std::string(
"rectangle" ) ) );
2441 == CANONICAL_VALUE( std::string(
"circle" ) ) );
2442 BOOST_CHECK( canonicalGraphicType(
MODEL_GRAPHIC_KIND::ARC ).value == CANONICAL_VALUE( std::string(
"arc" ) ) );
2444 == CANONICAL_VALUE( std::string(
"polyline" ) ) );
2446 == CANONICAL_VALUE( std::string(
"rectangle" ) ) );
2448 == CANONICAL_VALUE( std::string(
"circle" ) ) );
2451 == CANONICAL_VALUE( std::string(
"polyline" ) ) );
2453 BOOST_CHECK( canonicalLineStyle( 255 ).value == CANONICAL_VALUE( std::string(
"solid" ) ) );
2454 BOOST_CHECK( canonicalLineStyle( 0 ).value == CANONICAL_VALUE( std::string(
"dash" ) ) );
2457 BOOST_CHECK( canonicalFill(
true ).value == CANONICAL_VALUE( std::string(
"filled" ) ) );
2458 BOOST_CHECK( canonicalPinType( uint32_t( 3 ) ).value == CANONICAL_VALUE( std::string(
"bidirectional" ) ) );
2460 == CANONICAL_VALUE( std::string(
"bidirectional" ) ) );
2461 BOOST_CHECK( canonicalPinStyle( 3 ).value == CANONICAL_VALUE( std::string(
"inverted_clock" ) ) );
2462 BOOST_CHECK( canonicalPinStyle(
true,
true ).value == CANONICAL_VALUE( std::string(
"inverted_clock" ) ) );
2464 == CANONICAL_VALUE( std::string(
"center" ) ) );
2465 BOOST_CHECK( canonicalHorizontalJustification( 12 ).value == CANONICAL_VALUE( std::string(
"center" ) ) );
2466 BOOST_CHECK( canonicalVerticalJustification( 12 ).value == CANONICAL_VALUE( std::string(
"center" ) ) );
2468 == CANONICAL_VALUE( std::string(
"hierarchical" ) ) );
2477 auto graphic = std::ranges::find_if( definition.graphics,
2480 return aGraphic.line_style == 255;
2483 if( graphic != definition.graphics.end() )
2485 serializedGraphic = &*graphic;
2491 BOOST_CHECK( canonicalLineStyle( serializedGraphic->
line_style )
2498 SOURCE_PROVENANCE source{ wxS(
"mapping.sch" ), 0x000D, wxS(
"synthetic" ), 1, 0, 0, 0, 0 };
2499 auto string = [&](
const wxString& aText )
2510 definition.
source = source;
2511 definition.
pins.push_back( {
PIN_ID( 91 ), source, string( wxS(
"A1" ) ), string( wxS(
"CLK" ) ) } );
2512 model.definitions.push_back( definition );
2516 part.
gates.push_back( {
GATE_ID( 2 ), source, { definition.
id, source }, 1, { {
PIN_ID( 91 ), source } } } );
2517 model.partTypes.push_back( part );
2519 const auto records = normalizeBinaryModel(
model );
2520 auto mapping = std::ranges::find_if( records,
2521 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
2523 return aRecord.kind == CANONICAL_KIND::GATE_PIN_MAPPING;
2526 BOOST_CHECK( mapping->properties.at(
"pin_number" ).value == CANONICAL_VALUE( std::string(
"A1" ) ) );
2527 BOOST_CHECK( mapping->properties.at(
"pin_name" ).value == CANONICAL_VALUE( std::string(
"CLK" ) ) );
2528 BOOST_CHECK( !mapping->properties.contains(
"pin_id" ) );
2535 PADS_SCH_MODEL model = parser.
Parse( loadBinaryFixture(
"symbol_primitives.sch" ), wxS(
"symbol_primitives.sch" ) );
2538 BOOST_REQUIRE_EQUAL( definition.
graphics.size(), 6 );
2540 BOOST_REQUIRE_EQUAL( definition.
graphics[0].points.size(), 2 );
2551 BOOST_CHECK( definition.
graphics[3].arcClockwise );
2562 BOOST_REQUIRE_EQUAL( definition.
graphics[5].points.size(), 1 );
2568 BOOST_REQUIRE_EQUAL( definition.
pins.size(), 2 );
2574 std::vector<uint8_t> privateStroke = loadBinaryFixture(
"symbol_primitives.sch" );
2575 const size_t privatePiece =
2576 sheetControllerOffset( privateStroke, 4 ) + definition.
graphics[0].source.recordIndex * 6;
2577 writeU16( privateStroke, privatePiece + 4, 0xEF03 );
2578 PADS_SCH_MODEL preservedStroke = parser.
Parse( privateStroke, wxS(
"private-symbol-stroke.sch" ) );
2580 itemNamed( preservedStroke.
definitions, wxS(
"BATCHB_PRIMITIVES" ) );
2581 auto privateGraphic = std::ranges::find_if( preservedDefinition.
graphics,
2584 return aGraphic.source.controller == 4
2585 && aGraphic.source.recordIndex
2586 == definition.graphics[0].source.recordIndex;
2590 auto rawStroke = std::ranges::find_if( privateGraphic->properties,
2593 return aProperty.name.text == wxS(
"unsupported_graphic_stroke_width" );
2595 BOOST_REQUIRE( rawStroke != privateGraphic->properties.end() );
2600 BOOST_CHECK_EQUAL( propertyValue( privateGraphic->properties, wxS(
"preserved_graphic_presentation" ) ),
2608 std::vector<uint8_t> fixture = loadBinaryFixture(
"pin_styles.sch" );
2611 itemNamed( baseline.
definitions, wxS(
"BATCHB_PIN_STYLES" ) ).pins[0];
2614 writeU16( fixture, visibilityOffset, 0x8040 );
2615 PADS_SCH_MODEL visibilityOnly = parser.
Parse( fixture, wxS(
"terminal-visibility-only.sch" ) );
2617 itemNamed( visibilityOnly.
definitions, wxS(
"BATCHB_PIN_STYLES" ) ).pins[0];
2620 BOOST_CHECK( std::ranges::none_of( visibilityOnly.
diagnostics,
2623 return aDiagnostic.message.Contains(
2624 wxS(
"terminal number-offset presentation" ) )
2625 && aDiagnostic.source.controller == 8
2626 && aDiagnostic.source.recordIndex
2627 == baselinePin.source.recordIndex;
2630 writeU16( fixture, visibilityOffset, 0x0D40 );
2631 PADS_SCH_MODEL pairedPresentation = parser.
Parse( fixture, wxS(
"terminal-presentation-0d.sch" ) );
2633 itemNamed( pairedPresentation.
definitions, wxS(
"BATCHB_PIN_STYLES" ) ).pins[0];
2640 BOOST_CHECK( std::ranges::none_of( pairedPresentation.
diagnostics,
2643 return ( aDiagnostic.message.Contains(
2644 wxS(
"terminal name-offset presentation" ) )
2645 || aDiagnostic.message.Contains(
2646 wxS(
"terminal number-offset presentation" ) ) )
2647 && aDiagnostic.source.controller == 8
2648 && aDiagnostic.source.recordIndex
2649 == baselinePin.source.recordIndex;
2657 PADS_SCH_MODEL pins = parser.
Parse( loadBinaryFixture(
"pin_styles.sch" ), wxS(
"pin_styles.sch" ) );
2659 BOOST_REQUIRE_EQUAL( pinPart.
gates.size(), 1 );
2660 BOOST_REQUIRE_EQUAL( pinPart.
gates[0].pins.size(), 7 );
2663 BOOST_REQUIRE_EQUAL( pinDefinition.
pins.size(), 7 );
2670 BOOST_CHECK( !pinDefinition.
pins[6].presentation.visible );
2674 BOOST_CHECK_EQUAL( propertyValue( pinDefinition.
pins[0].properties, wxS(
"pin_name_height_half_mils" ) ),
2676 BOOST_CHECK_EQUAL( propertyValue( pinDefinition.
pins[0].properties, wxS(
"pin_number_height_half_mils" ) ),
2684 BOOST_CHECK( pinDefinition.
pins[2].presentation.visible );
2685 BOOST_CHECK( !pinDefinition.
pins[2].namePresentation.visible );
2686 BOOST_CHECK( !pinDefinition.
pins[6].namePresentation.visible );
2687 BOOST_CHECK( pinDefinition.
pins[6].numberPresentation.visible );
2688 BOOST_REQUIRE_EQUAL( pinPart.
signalPins.size(), 1 );
2692 PADS_SCH_MODEL multi = parser.
Parse( loadBinaryFixture(
"multigate.sch" ), wxS(
"multigate.sch" ) );
2694 BOOST_REQUIRE_EQUAL( multiPart.
gates.size(), 2 );
2701 BOOST_REQUIRE_EQUAL( alternatePart.
gates[0].alternateDefinitions.size(), 3 );
2702 BOOST_REQUIRE_EQUAL( multiPart.
signalPins.size(), 1 );
2705 PADS_SCH_MODEL connectorModel = parser.
Parse( loadBinaryFixture(
"connectors.sch" ), wxS(
"connectors.sch" ) );
2707 BOOST_REQUIRE_EQUAL( connector.
gates.size(), 1 );
2708 BOOST_REQUIRE_EQUAL( connector.
gates[0].decalGroupMembers.size(), 5 );
2709 BOOST_REQUIRE_EQUAL( connector.
gates[0].connectorPins.size(), 26 );
2717 for(
size_t pin = 0;
pin < connector.
gates[0].connectorPins.size(); ++
pin )
2738 BOOST_CHECK( controller7Record11214.
IsValid() );
2739 BOOST_CHECK( controller7Record12125.
IsValid() );
2740 BOOST_CHECK_NE( controller7Record11214.
Value(), controller7Record12125.
Value() );
2745 BOOST_REQUIRE_EQUAL( definition.
fields.size(), 4 );
2746 std::set<uint64_t> fieldIds;
2748 for(
size_t ordinal = 0; ordinal < definition.
fields.size(); ++ordinal )
2754 BOOST_CHECK( fieldIds.insert( field.
id.
Value() ).second );
2767 BOOST_CHECK( definition.
fields[0].value.text.empty() );
2782 BOOST_REQUIRE_EQUAL( partType.
fields.size(), 4 );
2784 for(
size_t i = 0; i < partType.
fields.size(); ++i )
2788 BOOST_CHECK( partType.
fields[i].presentation == definition.
fields[i].presentation );
2789 BOOST_CHECK( partType.
fields[i].id.IsValid() );
2792 BOOST_CHECK( fieldIds.insert( partType.
fields[i].id.Value() ).second );
2795 PADS_SCH_MODEL repeated = parser.
Parse( loadBinaryFixture(
"fields.sch" ), wxS(
"fields.sch" ) );
2805 parser.
Parse( loadBinaryFixture(
"placement_transform.sch" ), wxS(
"placement_transform.sch" ) );
2806 BOOST_REQUIRE_EQUAL(
model.placements.size(), 5 );
2808 const std::array<wxString, 5> references = { wxS(
"R1" ), wxS(
"R2" ), wxS(
"R3" ), wxS(
"R4" ), wxS(
"R5" ) };
2812 const std::array<int, 5> angles = { 0, 900, 0, 900, 900 };
2813 const std::array<bool, 5> mirrored = {
false,
false,
true,
true,
true };
2814 const std::array<uint16_t, 5> mirrorFlags = { 0, 0, 3, 3, 2 };
2815 const std::array<std::pair<uint16_t, int>, 4> orthogonalAngles = {
2816 std::pair<uint16_t, int>{ 0, 0 }, { 900, 900 }, { 1800, 1800 }, { 2700, 2700 }
2819 for(
const auto& [raw, normalized] : orthogonalAngles )
2821 std::vector<uint8_t> transform = loadBinaryFixture(
"placement_transform.sch" );
2822 writeU16( transform, sheetControllerOffset( transform, 15 ) + 0x24, raw );
2826 wxString::Format( wxS(
"%u" ), raw ) );
2829 for(
size_t i = 0; i <
model.placements.size(); ++i )
2839 wxString::Format( wxS(
"%d" ), angles[i] ) );
2841 wxString::Format( wxS(
"%u" ), mirrorFlags[i] ) );
2842 auto rawAngle = std::ranges::find_if( placement.
properties,
2845 return aProperty.name.text == wxS(
"raw_angle" );
2847 auto rawMirror = std::ranges::find_if( placement.
properties,
2850 return aProperty.name.text == wxS(
"raw_mirror" );
2861 BOOST_CHECK( placement.
gate->id.IsValid() );
2864 BOOST_CHECK_GE( placement.
fields.size(), 3 );
2879 auto placement = std::ranges::find_if( aModel.
placements,
2882 return aPlacement.reference.text == aReference;
2888 PADS_SCH_MODEL multi = parser.
Parse( loadBinaryFixture(
"multigate.sch" ), wxS(
"multigate.sch" ) );
2889 const MODEL_PLACEMENT& gateA = placementNamed( multi, wxS(
"U3-A" ) );
2890 const MODEL_PLACEMENT& gateB = placementNamed( multi, wxS(
"U3-B" ) );
2897 PADS_SCH_MODEL connectors = parser.
Parse( loadBinaryFixture(
"connectors.sch" ), wxS(
"connectors.sch" ) );
2898 const MODEL_PLACEMENT& connector = placementNamed( connectors, wxS(
"P1-1" ) );
2900 BOOST_CHECK_GE( connector.
fields.size(), 4 );
2902 BOOST_CHECK( connector.
fields[2].value.text.empty() );
2904 BOOST_CHECK( connector.
fields[3].value.text.empty() );
2912 BOOST_REQUIRE_EQUAL(
model.placements.size(), 1 );
2914 BOOST_REQUIRE_GE( placement.
fields.size(), 3 );
2940 auto attributeIndex = std::ranges::find_if( placement.
fields[2].properties,
2943 return aProperty.name.text == wxS(
"component_attribute_index" );
2950 for(
size_t i = 0; i < placement.
fields.size(); ++i )
2962 parser.
Parse( loadBinaryFixture(
"placement_transform.sch" ), wxS(
"duplicate-placement.sch" ) );
2967 return aError.What().Contains( wxS(
"duplicate placement ID" ) )
2968 && aError.What().Contains( wxS(
"controller 15" ) )
2969 && aError.What().Contains( wxS(
"first at" ) );
2972 std::vector<uint8_t> unresolvedPart = loadBinaryFixture(
"placement_transform.sch" );
2973 writeU16( unresolvedPart, sheetControllerOffset( unresolvedPart, 15 ) + 0x42, 20 );
2974 BOOST_CHECK_EXCEPTION( parser.
Parse( unresolvedPart, wxS(
"placement-part.sch" ) ),
IO_ERROR,
2977 return aError.
What().Contains( wxS(
"targets definition object class" ) )
2978 && aError.
What().Contains( wxS(
"controller 15" ) );
2981 std::vector<uint8_t> wrongGroup = loadBinaryFixture(
"placement_transform.sch" );
2982 writeU32( wrongGroup, sheetControllerOffset( wrongGroup, 15 ) + 0x1C, 0xFFFFFFFF );
2983 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongGroup, wxS(
"placement-group.sch" ) ),
IO_ERROR,
2986 return aError.
What().Contains( wxS(
"component-group handle" ) );
2989 std::vector<uint8_t> wrongDecal = loadBinaryFixture(
"placement_transform.sch" );
2990 writeU16( wrongDecal, sheetControllerOffset( wrongDecal, 15 ) + 0x44, 0 );
2991 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongDecal, wxS(
"placement-decal.sch" ) ),
IO_ERROR,
2994 return aError.
What().Contains( wxS(
"different object classes" ) );
2997 std::vector<uint8_t> wrongGate = loadBinaryFixture(
"multigate.sch" );
2998 writeU16( wrongGate, sheetControllerOffset( wrongGate, 15 ) + 136 + 0x4A, 7 );
2999 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongGate, wxS(
"placement-gate.sch" ) ),
IO_ERROR,
3002 return aError.
What().Contains( wxS(
"gate handle targets definition object class" ) );
3005 std::vector<uint8_t> wrongPin = loadBinaryFixture(
"placement_transform.sch" );
3006 writeU16( wrongPin, sheetControllerOffset( wrongPin, 16 ) + 4, 0xFFFF );
3007 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongPin, wxS(
"placement-pin.sch" ) ),
IO_ERROR,
3010 return aError.
What().Contains( wxS(
"placed-pin handle" ) );
3013 std::vector<uint8_t> wrongField = loadBinaryFixture(
"fields.sch" );
3014 BOOST_REQUIRE_GT( readU32( wrongField, 0x20 + 7 * 28 + 8 ), 10 );
3015 BOOST_REQUIRE_LE( readU32( wrongField, 0x20 + 19 * 28 + 8 ), 10 );
3016 writeU16( wrongField, sheetControllerOffset( wrongField, 17 ), 10 );
3017 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongField, wxS(
"placement-field.sch" ) ),
IO_ERROR,
3020 return aError.
What().Contains( wxS(
"placement font handle" ) );
3023 std::vector<uint8_t> wrongAttribute = loadBinaryFixture(
"fields.sch" );
3024 writeU16( wrongAttribute, sheetControllerOffset( wrongAttribute, 17 ) + 16, 0xFFFE );
3025 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongAttribute, wxS(
"placement-field-attribute.sch" ) ),
IO_ERROR,
3028 return aError.
What().Contains( wxS(
"attribute index leaves component group" ) )
3029 && aError.
What().Contains( wxS(
"controller 17" ) );
3033 parser.
Parse( loadBinaryFixture(
"placement_transform.sch" ), wxS(
"placement-sheet.sch" ) );
3034 const uint32_t wrongClassSheet = wrongSheet.
definitions.back().id.Value();
3038 return aSheet.id.Value() == wrongClassSheet;
3040 wrongSheet.placements[0].sheet = {
SHEET_ID( wrongClassSheet ), wrongSheet.placements[0].source };
3041 BOOST_CHECK_EXCEPTION( wrongSheet.ValidateOrThrow(),
IO_ERROR,
3044 return aError.What().Contains( wxS(
"unresolved placement sheet reference" ) );
3047 std::vector<uint8_t> unknownTransform = loadBinaryFixture(
"placement_transform.sch" );
3048 writeU16( unknownTransform, sheetControllerOffset( unknownTransform, 15 ) + 0x24, 123 );
3049 writeU16( unknownTransform, sheetControllerOffset( unknownTransform, 15 ) + 0x26, 9 );
3056 return aDiagnostic.message.Contains(
3057 wxS(
"unknown placement transform" ) );
3061 std::vector<uint8_t> boundaryTransform = loadBinaryFixture(
"placement_transform.sch" );
3062 writeU16( boundaryTransform, sheetControllerOffset( boundaryTransform, 15 ) + 0x24, 3600 );
3063 writeU16( boundaryTransform, sheetControllerOffset( boundaryTransform, 15 ) + 0x26, 1 );
3064 PADS_SCH_MODEL boundary = parser.
Parse( boundaryTransform, wxS(
"placement-transform-boundary.sch" ) );
3072 return aDiagnostic.message.Contains(
3073 wxS(
"unknown placement transform" ) );
3083 wxS(
"placement_transform_options.sch" ) );
3085 BOOST_REQUIRE_EQUAL(
model.placements.size(), 8 );
3087 const std::array<int, 4> angles = { 0, 900, 1800, 2700 };
3089 for(
size_t i = 0; i <
model.placements.size(); ++i )
3092 const uint16_t expectedMirror = i / angles.size();
3099 wxString::Format( wxS(
"%d" ), angles[i % angles.size()] ) );
3101 wxString::Format( wxS(
"%u" ), expectedMirror ) );
3109 const std::array<std::string, 7> fixtures = {
3110 "placement_transform",
"placement_transform_options",
"fields",
"connectors",
3111 "multigate",
"field_justification",
"pin_justification"
3114 for(
const std::string& fixture : fixtures )
3117 binaryParser.
Parse( loadBinaryFixture( fixture +
".sch" ), wxString::FromUTF8( fixture +
".sch" ) );
3122 for(
size_t placementIndex = 0; placementIndex < binary.
placements.size(); ++placementIndex )
3132 auto part = std::ranges::find_if( binary.
partTypes,
3135 return aPart.id == binaryPlacement.partType.id;
3140 auto gate = std::ranges::find_if( part->gates,
3143 return aGate.id == binaryPlacement.gate->id;
3147 auto definition = std::ranges::find_if( binary.
definitions,
3150 return aDefinition.id == binaryPlacement.definition.id;
3154 BOOST_CHECK( !propertyValue( binaryPlacement.
properties, wxS(
"decal_handle" ) ).
empty() );
3155 BOOST_REQUIRE_GE( binaryPlacement.
fields.size(), asciiPlacement.
attributes.size() );
3159 BOOST_REQUIRE_EQUAL( binaryPlacement.
pins.size(), asciiPlacement.
pin_overrides.size() );
3161 for(
size_t pinIndex = 0; pinIndex < binaryPlacement.
pins.size(); ++pinIndex )
3170 for(
size_t fieldIndex = 0; fieldIndex < asciiPlacement.
attributes.size(); ++fieldIndex )
3175 const wxString asciiValue = fieldIndex == 0 ? wxString::FromUTF8( asciiPlacement.
reference )
3176 : fieldIndex == 1 ? wxString::FromUTF8( asciiPlacement.
part_type )
3177 : wxString::FromUTF8( asciiField.
value );
3184 const bool expectedVisible = fieldIndex == 0 ? binaryPlacement.
referenceVisible
3190 wxString::FromUTF8( asciiField.
font_name ).StartsWith( wxS(
"Bold " ) ) );
3192 wxString::FromUTF8( asciiField.
font_name ).StartsWith( wxS(
"Italic " ) ) );
3195 const uint16_t horizontal = justification >= 8 ? justification - 8
3196 : justification >= 2 ? justification - 2
3208 if( fieldIndex >= 2 )
3211 wxString::Format( wxS(
"%d" ), asciiField.
visibility ) );
3212 const wxString expectedAttributeIndex = fixture ==
"fields" || fixture ==
"field_justification"
3213 || fixture ==
"placement_transform_options"
3217 expectedAttributeIndex );
3229 parser.
Parse( loadBinaryFixture(
"connectivity_topology.sch" ), wxS(
"connectivity_topology.sch" ) );
3231 BOOST_REQUIRE_EQUAL(
model.nets.size(), 9 );
3234 BOOST_REQUIRE_EQUAL( itemNamed(
model.nets, wxS(
"T_NET" ) ).connections.size(), 3 );
3237 BOOST_REQUIRE_EQUAL( horizontal.
vertices.size(), 2 );
3238 BOOST_REQUIRE_EQUAL( horizontal.
endpoints.size(), 2 );
3247 BOOST_REQUIRE_EQUAL( minimal.
nets.size(), 1 );
3248 BOOST_REQUIRE_EQUAL( minimal.
nets[0].connections.size(), 1 );
3263 parser.
Parse( loadBinaryFixture(
"connectivity_topology.sch" ), wxS(
"connectivity_topology.sch" ) );
3265 BOOST_REQUIRE_EQUAL(
model.labels.size(), 16 );
3271 auto global = std::ranges::find_if(
model.labels,
3274 return aLabel.kind == MODEL_LABEL_KIND::GLOBAL;
3283 parser.
Parse( loadBinaryFixture(
"multisheet_connectivity.sch" ), wxS(
"multisheet_connectivity.sch" ) );
3287 return aLabel.kind == MODEL_LABEL_KIND::GLOBAL
3288 && aLabel.text.text == wxS(
"CROSS_SHEET" );
3291 std::vector<const MODEL_LABEL*> crossSheetLabels;
3296 crossSheetLabels.push_back( &label );
3299 BOOST_REQUIRE_EQUAL( crossSheetLabels.size(), 2 );
3300 std::ranges::sort( crossSheetLabels,
3305 BOOST_REQUIRE_EQUAL( crossSheetLabels[0]->linkedSheets.size(), 1 );
3306 BOOST_REQUIRE_EQUAL( crossSheetLabels[1]->linkedSheets.size(), 1 );
3318 parser.
Parse( loadBinaryFixture(
"connectivity_topology.sch" ), wxS(
"connectivity_topology.sch" ) );
3320 BOOST_REQUIRE_EQUAL(
model.buses.size(), 1 );
3323 BOOST_REQUIRE_EQUAL( bus.
vertices.size(), 3 );
3328 BOOST_REQUIRE_EQUAL( bus.
entries.size(), 3 );
3329 BOOST_REQUIRE_EQUAL( bus.
aliases.size(), 1 );
3330 BOOST_REQUIRE_EQUAL( bus.
memberNets.size(), 3 );
3346 BOOST_REQUIRE_EQUAL(
model.sheets.size(), 1 );
3347 BOOST_REQUIRE_EQUAL(
model.worksheets.size(), 1u );
3348 BOOST_REQUIRE_EQUAL(
model.worksheets[0].graphics.size(), 86u );
3349 BOOST_REQUIRE_EQUAL(
model.graphics.size(), 6u );
3353 return aGraphic.kind == MODEL_GRAPHIC_KIND::LINE;
3356 BOOST_CHECK_GE( std::ranges::count_if(
model.worksheets[0].graphics,
3359 return aGraphic.kind == MODEL_GRAPHIC_KIND::POLYLINE;
3365 return aGraphic.kind == MODEL_GRAPHIC_KIND::TEXT;
3368 auto titleText = std::ranges::find_if(
model.worksheets[0].graphics,
3371 return aGraphic.kind == MODEL_GRAPHIC_KIND::TEXT
3372 && aGraphic.text.text == wxS(
"TITLE:" );
3377 auto titleGroup = std::ranges::find_if( titleText->properties,
3380 return aProperty.name.text == wxS(
"page_graphic_group" );
3383 const uint16_t titleTextCount =
3384 readU16( loadBinaryFixture(
"page_graphics.sch" ), titleGroup->source.absoluteOffset + 64 );
3385 BOOST_REQUIRE_GT( titleTextCount, 1u );
3387 std::vector<uint8_t> repeatedPredecessor = loadBinaryFixture(
"page_graphics.sch" );
3388 const uint16_t lastTextRecord = readU16( repeatedPredecessor, titleGroup->source.absoluteOffset + 66 );
3389 writeU16( repeatedPredecessor, sheetControllerOffset( repeatedPredecessor, 1 ) + lastTextRecord * 32 + 24,
3391 BOOST_CHECK_EXCEPTION( parser.
Parse( repeatedPredecessor, wxS(
"page-text-cycle.sch" ) ),
IO_ERROR,
3394 return aError.
What().Contains(
3395 wxS(
"page-text predecessor repeats controller 1 record" ) );
3398 std::vector<uint8_t> noncontiguousPredecessor = loadBinaryFixture(
"page_graphics.sch" );
3399 const size_t textController = sheetControllerOffset( noncontiguousPredecessor, 1 );
3400 const uint16_t predecessor1 = readU16( noncontiguousPredecessor, textController + lastTextRecord * 32 + 24 );
3401 const uint16_t predecessor2 = readU16( noncontiguousPredecessor, textController + predecessor1 * 32 + 24 );
3402 const uint16_t predecessor3 = readU16( noncontiguousPredecessor, textController + predecessor2 * 32 + 24 );
3403 writeU16( noncontiguousPredecessor, textController + lastTextRecord * 32 + 24, predecessor2 );
3404 writeU16( noncontiguousPredecessor, textController + predecessor2 * 32 + 24, predecessor1 );
3405 writeU16( noncontiguousPredecessor, textController + predecessor1 * 32 + 24, predecessor3 );
3406 PADS_SCH_MODEL noncontiguous = parser.
Parse( noncontiguousPredecessor, wxS(
"page-text-noncontiguous.sch" ) );
3407 std::vector<size_t> recoveredRecords;
3414 && propertyValue( graphic.
properties, wxS(
"page_graphic_group" ) ) == titleGroup->value.text )
3420 BOOST_REQUIRE_EQUAL( recoveredRecords.size(), titleTextCount );
3421 BOOST_CHECK_EQUAL( recoveredRecords[recoveredRecords.size() - 3], predecessor1 );
3422 BOOST_CHECK_EQUAL( recoveredRecords[recoveredRecords.size() - 2], predecessor2 );
3425 std::vector<const MODEL_PAGE_GRAPHIC*> custom;
3429 if( propertyValue( graphic.graphic.
properties, wxS(
"page_graphic_group" ) ) == wxS(
"BATCHB_PAGE_GRAPHICS" ) )
3431 custom.push_back( &graphic );
3435 BOOST_REQUIRE_EQUAL( custom.size(), 6 );
3438 return std::ranges::find_if( custom,
3446 BOOST_REQUIRE_EQUAL( ( *line )->graphic.points.size(), 2 );
3451 std::vector<uint8_t> unprovedLineStyle = loadBinaryFixture(
"page_graphics.sch" );
3452 unprovedLineStyle[( *line )->graphic.source.absoluteOffset + 1] = 2;
3453 PADS_SCH_MODEL unprovedStyleModel = parser.
Parse( unprovedLineStyle, wxS(
"unproved-line-style.sch" ) );
3454 auto unprovedStyleGraphic = std::ranges::find_if( unprovedStyleModel.
graphics,
3457 return aGraphic.graphic.source.recordIndex
3458 == ( *line )->graphic.source.recordIndex;
3465 return aDiagnostic.message.Contains(
3466 wxS(
"page graphic line style 2" ) );
3476 BOOST_CHECK( ( *arc )->graphic.arcClockwise );
3483 BOOST_CHECK_EQUAL( ( *text )->graphic.presentation.font.text, wxS(
"Default Font" ) );
3488 auto pageRecords = []( std::vector<CANONICAL_SEMANTIC_RECORD> aRecords )
3490 std::erase_if( aRecords,
3491 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
3493 auto group = aRecord.properties.find(
"page_graphic_group" );
3494 return aRecord.kind != CANONICAL_KIND::GRAPHIC ||
group == aRecord.properties.end()
3495 ||
group->second.value != CANONICAL_VALUE( std::string(
"BATCHB_PAGE_GRAPHICS" ) );
3499 auto expectedPage = pageRecords( normalizeAsciiModel( ascii ) );
3500 auto actualPage = pageRecords( normalizeBinaryModel(
model ) );
3501 BOOST_CHECK( snapshotsMatch( expectedPage, actualPage,
3504 std::vector<uint8_t> privateStroke = loadBinaryFixture(
"page_graphics.sch" );
3505 const size_t privatePiece = sheetControllerOffset( privateStroke, 4 ) + 81 * 6;
3506 writeU16( privateStroke, privatePiece + 4, 0xEF03 );
3507 PADS_SCH_MODEL preservedStroke = parser.
Parse( privateStroke, wxS(
"private-stroke.sch" ) );
3508 auto privateGraphic = std::ranges::find_if( preservedStroke.
graphics,
3511 return aGraphic.graphic.source.controller == 4
3512 && aGraphic.graphic.source.recordIndex == 81;
3516 auto rawStroke = std::ranges::find_if( privateGraphic->graphic.properties,
3519 return aProperty.name.text == wxS(
"unsupported_graphic_stroke_width" );
3521 BOOST_REQUIRE( rawStroke != privateGraphic->graphic.properties.end() );
3528 BOOST_CHECK_EQUAL( propertyValue( privateGraphic->graphic.properties, wxS(
"preserved_graphic_presentation" ) ),
3533 return aDiagnostic.source == rawStroke->source && aDiagnostic.property
3534 && aDiagnostic.property->name == rawStroke->name.text
3535 && aDiagnostic.property->disposition == rawStroke->disposition;
3544 const PADS_SCH_MODEL minimal = parser.
Parse( loadBinaryFixture(
"minimal_v13.sch" ), wxS(
"minimal_v13.sch" ) );
3546 BOOST_REQUIRE_EQUAL( minimal.
worksheets.size(), 1u );
3549 BOOST_CHECK( minimal.
graphics.empty() );
3551 const PADS_SCH_MODEL page = parser.
Parse( loadBinaryFixture(
"page_graphics.sch" ), wxS(
"page_graphics.sch" ) );
3552 BOOST_REQUIRE_EQUAL( page.
worksheets.size(), 1u );
3555 BOOST_REQUIRE_EQUAL( page.
graphics.size(), 6u );
3556 BOOST_CHECK( std::ranges::all_of( page.
graphics,
3559 return propertyValue( aGraphic.graphic.properties,
3560 wxS(
"page_graphic_group" ) )
3561 == wxS(
"BATCHB_PAGE_GRAPHICS" );
3564 std::vector<uint8_t> distinct = loadBinaryFixture(
"page_graphics.sch" );
3565 const std::string duplicateName =
"BATCHB_SIZEB";
3566 auto duplicateRecord = std::search( distinct.begin(), distinct.end(), duplicateName.begin(), duplicateName.end() );
3568 const size_t duplicateOffset = std::distance( distinct.begin(), duplicateRecord );
3569 const size_t firstVertex = readU32( distinct, duplicateOffset + 0x34 );
3570 const size_t vertexOffset = sheetControllerOffset( distinct, 5 ) + firstVertex * 8;
3571 writeU16( distinct, vertexOffset, readU16( distinct, vertexOffset ) + 2 );
3573 const PADS_SCH_MODEL distinctPage = parser.
Parse( distinct, wxS(
"distinct-page-graphics.sch" ) );
3574 BOOST_REQUIRE_EQUAL( distinctPage.
worksheets.size(), 1u );
3580 return propertyValue( aGraphic.graphic.properties,
3581 wxS(
"page_graphic_group" ) )
3582 == wxS(
"BATCHB_SIZEB" );
3588 return aDiagnostic.message.Contains(
3589 wxS(
"distinct worksheet layout preserved" ) );
3598 std::vector<uint8_t> wrongEndpoint = loadBinaryFixture(
"connectivity_topology.sch" );
3599 const size_t connection = sheetControllerOffset( wrongEndpoint, 21 );
3600 writeU16( wrongEndpoint, connection + 12, 0x1000 );
3601 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongEndpoint, wxS(
"wrong-endpoint.sch" ) ),
IO_ERROR,
3604 return aError.
What().Contains( wxS(
"controller 21" ) )
3605 && aError.
What().Contains( wxS(
"wrong endpoint object class" ) );
3608 std::vector<uint8_t> unresolvedEndpoint = loadBinaryFixture(
"connectivity_topology.sch" );
3609 writeU16( unresolvedEndpoint, connection + 12, 0x2FFF );
3610 BOOST_CHECK_EXCEPTION( parser.
Parse( unresolvedEndpoint, wxS(
"unresolved-endpoint.sch" ) ),
IO_ERROR,
3613 return aError.
What().Contains( wxS(
"controller 21" ) )
3614 && aError.
What().Contains( wxS(
"unresolved off-page endpoint" ) );
3617 std::vector<uint8_t> unresolvedPlacement = loadBinaryFixture(
"minimal_v13.sch" );
3618 const size_t minimalConnection = sheetControllerOffset( unresolvedPlacement, 21 );
3619 BOOST_REQUIRE_EQUAL( readU16( unresolvedPlacement, minimalConnection + 12 ), 0u );
3620 writeU16( unresolvedPlacement, minimalConnection + 12, 0x0FFF );
3621 BOOST_CHECK_EXCEPTION( parser.
Parse( unresolvedPlacement, wxS(
"unresolved-placement.sch" ) ),
IO_ERROR,
3624 return aError.
What().Contains( wxS(
"controller 21" ) )
3625 && aError.
What().Contains( wxS(
"unresolved placement endpoint" ) );
3628 std::vector<uint8_t> duplicateMembership = loadBinaryFixture(
"connectivity_topology.sch" );
3629 const size_t globalNets = outerControllerOffset( duplicateMembership, 8 );
3630 writeU32( duplicateMembership, globalNets + 2 * 88 + 4, 1 );
3631 BOOST_CHECK_EXCEPTION( parser.
Parse( duplicateMembership, wxS(
"duplicate-membership.sch" ) ),
IO_ERROR,
3634 return aError.
What().Contains( wxS(
"controller 8" ) )
3635 && aError.
What().Contains( wxS(
"duplicate net sheet-membership" ) );
3638 std::vector<uint8_t> wrongNetClass = loadBinaryFixture(
"connectivity_topology.sch" );
3639 const size_t wrongNetConnection = sheetControllerOffset( wrongNetClass, 21 );
3640 writeU32( wrongNetClass, wrongNetConnection + 8, 0 );
3641 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongNetClass, wxS(
"wrong-net-class.sch" ) ),
IO_ERROR,
3644 return aError.
What().Contains( wxS(
"controller 21" ) )
3645 && aError.
What().Contains( wxS(
"wrong or unresolved object class" ) );
3648 std::vector<uint8_t> wrongBusClass = loadBinaryFixture(
"connectivity_topology.sch" );
3649 const size_t wrongBus = sheetControllerOffset( wrongBusClass, 18 );
3650 writeU32( wrongBusClass, wrongBus + 8, 1 );
3651 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongBusClass, wxS(
"wrong-bus-class.sch" ) ),
IO_ERROR,
3654 return aError.
What().Contains( wxS(
"controller 18" ) )
3655 && aError.
What().Contains( wxS(
"wrong or unresolved object class" ) );
3658 std::vector<uint8_t> wrongSheet = loadBinaryFixture(
"multisheet_connectivity.sch" );
3659 const size_t sheetMembership = outerControllerOffset( wrongSheet, 4 );
3660 writeU16( wrongSheet, sheetMembership + 2, 2 );
3661 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongSheet, wxS(
"wrong-net-sheet.sch" ) ),
IO_ERROR,
3664 return aError.
What().Contains( wxS(
"controller 8" ) )
3665 && aError.
What().Contains( wxS(
"wrong sheet object class" ) );
3668 std::vector<uint8_t> busCycle = loadBinaryFixture(
"connectivity_topology.sch" );
3669 const size_t bus = sheetControllerOffset( busCycle, 18 );
3670 const size_t offpage = sheetControllerOffset( busCycle, 20 );
3671 const uint16_t tailHandle = readU16( busCycle, bus + 24 );
3672 BOOST_REQUIRE_EQUAL( tailHandle >> 12, 2 );
3673 const uint16_t tailRecord = tailHandle & 0x0FFF;
3674 writeU16( busCycle, offpage + tailRecord * 32 + 4, tailHandle );
3675 BOOST_CHECK_EXCEPTION( parser.
Parse( busCycle, wxS(
"bus-entry-cycle.sch" ) ),
IO_ERROR,
3678 return aError.
What().Contains( wxS(
"controller 18" ) )
3679 && aError.
What().Contains( wxS(
"cyclic bus-entry" ) );
3682 std::vector<uint8_t> unresolvedLabelConnection = loadBinaryFixture(
"connectivity_topology.sch" );
3683 const size_t label = sheetControllerOffset( unresolvedLabelConnection, 20 );
3684 writeU16( unresolvedLabelConnection, label + 8, 0xFFFF );
3685 BOOST_CHECK_EXCEPTION( parser.
Parse( unresolvedLabelConnection, wxS(
"unresolved-label-connection.sch" ) ),
3689 return aError.
What().Contains( wxS(
"controller 20" ) )
3690 && aError.
What().Contains( wxS(
"off-page net handle leaves controller 21" ) );
3693 std::vector<uint8_t> wrongJunctionOwner = loadBinaryFixture(
"connectivity_topology.sch" );
3694 const size_t junction = sheetControllerOffset( wrongJunctionOwner, 19 );
3695 const uint16_t junctionOwner = readU16( wrongJunctionOwner, junction + 8 );
3696 BOOST_REQUIRE_GT( sheetControllerCount( wrongJunctionOwner, 21 ), 1 );
3697 writeU16( wrongJunctionOwner, junction + 8, junctionOwner == 0 ? 1 : 0 );
3698 const wxString junctionOwnerOffset =
3699 wxString::Format( wxS(
"offset 0x%llX" ),
static_cast<unsigned long long>( junction + 8 ) );
3700 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongJunctionOwner, wxS(
"wrong-junction-owner.sch" ) ),
IO_ERROR,
3703 return aError.
What().Contains( wxS(
"controller 19" ) )
3704 && aError.
What().Contains( wxS(
"does not point back" ) )
3705 && aError.
What().Contains( junctionOwnerOffset );
3708 std::vector<uint8_t> crossNetJunction = loadBinaryFixture(
"connectivity_topology.sch" );
3709 const size_t crossNetJunctions = sheetControllerOffset( crossNetJunction, 19 );
3710 const size_t crossNetConnections = sheetControllerOffset( crossNetJunction, 21 );
3711 const uint16_t tiedDotHandle = 0x3000;
3712 const uint16_t tiedDotOwner = readU16( crossNetJunction, crossNetJunctions + 8 );
3713 size_t sharedConnection = std::numeric_limits<size_t>::max();
3714 uint32_t differentNet = std::numeric_limits<uint32_t>::max();
3715 const uint32_t ownerNet = readU32( crossNetJunction, crossNetConnections + tiedDotOwner * 40 + 8 );
3717 for(
size_t record = 0; record < sheetControllerCount( crossNetJunction, 21 ); ++record )
3719 const size_t offset = crossNetConnections + record * 40;
3721 if( record != tiedDotOwner
3722 && ( readU16( crossNetJunction, offset + 12 ) == tiedDotHandle
3723 || readU16( crossNetJunction, offset + 14 ) == tiedDotHandle ) )
3725 sharedConnection = record;
3728 if( readU32( crossNetJunction, offset + 8 ) != ownerNet )
3729 differentNet = readU32( crossNetJunction, offset + 8 );
3732 BOOST_REQUIRE_NE( sharedConnection, std::numeric_limits<size_t>::max() );
3733 BOOST_REQUIRE_NE( differentNet, std::numeric_limits<uint32_t>::max() );
3734 writeU32( crossNetJunction, crossNetConnections + sharedConnection * 40 + 8, differentNet );
3735 const wxString crossNetOffset =
3736 wxString::Format( wxS(
"offset 0x%llX" ),
static_cast<unsigned long long>( crossNetJunctions + 8 ) );
3737 BOOST_CHECK_EXCEPTION( parser.
Parse( crossNetJunction, wxS(
"cross-net-junction.sch" ) ),
IO_ERROR,
3740 return aError.
What().Contains( wxS(
"controller 19" ) )
3741 && aError.
What().Contains( wxS(
"shared across different nets" ) )
3742 && aError.
What().Contains( crossNetOffset );
3745 std::vector<uint8_t> wrongOffpageOwner = loadBinaryFixture(
"connectivity_topology.sch" );
3746 const size_t offpageOwner = sheetControllerOffset( wrongOffpageOwner, 20 );
3747 const uint16_t connectionOwner = readU16( wrongOffpageOwner, offpageOwner + 8 );
3748 writeU16( wrongOffpageOwner, offpageOwner + 8, connectionOwner == 0 ? 1 : 0 );
3749 const wxString offpageOwnerOffset =
3750 wxString::Format( wxS(
"offset 0x%llX" ),
static_cast<unsigned long long>( offpageOwner + 8 ) );
3751 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongOffpageOwner, wxS(
"wrong-offpage-owner.sch" ) ),
IO_ERROR,
3754 return aError.
What().Contains( wxS(
"controller 20" ) )
3755 && aError.
What().Contains( wxS(
"does not point back" ) )
3756 && aError.
What().Contains( offpageOwnerOffset );
3759 std::vector<uint8_t> crossNetOffpage = loadBinaryFixture(
"connectivity_topology.sch" );
3760 const size_t crossNetOffpages = sheetControllerOffset( crossNetOffpage, 20 );
3761 const size_t crossNetOffpageConnections = sheetControllerOffset( crossNetOffpage, 21 );
3762 const size_t crossNetVertices = sheetControllerOffset( crossNetOffpage, 22 );
3763 const uint16_t offpageConnection = readU16( crossNetOffpage, crossNetOffpages + 8 );
3764 const uint32_t offpageNet = readU32( crossNetOffpage, crossNetOffpageConnections + offpageConnection * 40 + 8 );
3765 size_t foreignConnection = std::numeric_limits<size_t>::max();
3767 for(
size_t record = 0; record < sheetControllerCount( crossNetOffpage, 21 ); ++record )
3769 if( readU32( crossNetOffpage, crossNetOffpageConnections + record * 40 + 8 ) != offpageNet )
3771 foreignConnection = record;
3776 BOOST_REQUIRE_NE( foreignConnection, std::numeric_limits<size_t>::max() );
3777 const size_t foreignOffset = crossNetOffpageConnections + foreignConnection * 40;
3778 const size_t firstVertex = crossNetVertices + readU32( crossNetOffpage, foreignOffset + 4 ) * 8;
3779 writeU16( crossNetOffpage, foreignOffset + 12, 0x2000 );
3780 writeU16( crossNetOffpage, firstVertex + 4, readU16( crossNetOffpage, crossNetOffpages + 22 ) );
3781 writeU16( crossNetOffpage, firstVertex + 6, readU16( crossNetOffpage, crossNetOffpages + 24 ) );
3782 BOOST_CHECK_EXCEPTION( parser.
Parse( crossNetOffpage, wxS(
"cross-net-offpage.sch" ) ),
IO_ERROR,
3785 return aError.
What().Contains( wxS(
"controller 20" ) )
3786 && aError.
What().Contains( wxS(
"shared across different nets" ) );
3794 std::vector<CANONICAL_SEMANTIC_RECORD> allActual;
3796 for(
const std::string& fixture :
3797 {
"minimal_v13",
"placement_transform",
"fields",
"connectors",
"text_encoding",
"page_graphics",
3798 "connectivity_topology",
"multisheet_connectivity",
"symbol_primitives",
"pin_styles",
"multigate" } )
3801 binaryParser.
Parse( loadBinaryFixture( fixture +
".sch" ), wxString::FromUTF8( fixture +
".sch" ) );
3805 auto task10 = []( std::vector<CANONICAL_SEMANTIC_RECORD> aRecords )
3807 std::erase_if( aRecords,
3808 [](
const CANONICAL_SEMANTIC_RECORD& aRecord )
3810 return aRecord.kind != CANONICAL_KIND::NET && aRecord.kind != CANONICAL_KIND::CONNECTION
3811 && aRecord.kind != CANONICAL_KIND::BUS
3812 && aRecord.kind != CANONICAL_KIND::BUS_ENTRY
3813 && aRecord.kind != CANONICAL_KIND::BUS_ALIAS
3814 && aRecord.kind != CANONICAL_KIND::BUS_MEMBER
3815 && aRecord.kind != CANONICAL_KIND::LABEL
3816 && aRecord.kind != CANONICAL_KIND::JUNCTION
3817 && ( aRecord.kind != CANONICAL_KIND::GRAPHIC
3818 || !aRecord.properties.contains(
"page_graphic_group" )
3819 || aRecord.properties.at(
"page_graphic_group" ).value
3820 != CANONICAL_VALUE( std::string(
"BATCHB_PAGE_GRAPHICS" ) ) );
3826 const SNAPSHOT_ALLOWLIST sourceProperties = {
3847 BOOST_REQUIRE_GE( connection.
vertices.size(), 2 );
3848 BOOST_REQUIRE_EQUAL( connection.
endpoints.size(), 2 );
3858 auto expected = task10( normalizeAsciiModel( ascii ) );
3859 auto actual = task10( normalizeBinaryModel( binary ) );
3860 BOOST_CHECK( snapshotsMatch(
expected,
actual, sourceProperties ) );
3861 allActual.insert( allActual.end(),
actual.begin(),
actual.end() );
3865 BOOST_CHECK( task10SourcePropertiesPresent( allActual ) );
3866 auto missingRawKind = allActual;
3868 auto label = std::ranges::find( missingRawKind, CANONICAL_KIND::LABEL, &CANONICAL_SEMANTIC_RECORD::kind );
3870 label->properties.erase(
"offpage_variant" );
3872 BOOST_CHECK( !task10SourcePropertiesPresent( missingRawKind ) );
3874 for(
const std::string& endpoint :
3875 {
"endpoint_0_raw_endpoint_handle",
"endpoint_1_raw_endpoint_handle",
"endpoint_0_raw_endpoint_relationship",
3876 "endpoint_1_raw_endpoint_relationship" } )
3878 auto missingEndpoint = allActual;
3880 std::ranges::find( missingEndpoint, CANONICAL_KIND::CONNECTION, &CANONICAL_SEMANTIC_RECORD::kind );
3882 connection->properties.erase( endpoint );
3883 BOOST_CHECK( !task10SourcePropertiesPresent( missingEndpoint ) );
3891 std::vector<uint8_t> wrongClass = loadBinaryFixture(
"symbol_primitives.sch" );
3892 size_t usedDecals = sheetControllerOffset( wrongClass, 7 );
3893 writeU32( wrongClass, usedDecals + 104 * 108 + 48, 8 );
3894 BOOST_CHECK_EXCEPTION( parser.
Parse( wrongClass, wxS(
"wrong-class.sch" ) ),
IO_ERROR,
3897 wxString message = aError.
What();
3898 return message.Contains( wxS(
"wrong object class" ) )
3899 && message.Contains( wxS(
"controller 7" ) )
3900 && message.Contains( wxS(
"record 104" ) );
3903 std::vector<uint8_t> unresolved = loadBinaryFixture(
"multigate.sch" );
3904 size_t gates = sheetControllerOffset( unresolved, 10 );
3905 writeU16( unresolved, gates + 12, 0xFFFF );
3906 BOOST_CHECK_EXCEPTION( parser.
Parse( unresolved, wxS(
"unresolved.sch" ) ),
IO_ERROR,
3909 return aError.
What().Contains( wxS(
"unresolved symbol definition reference" ) );
3912 std::vector<uint8_t> badPinHandle = loadBinaryFixture(
"symbol_primitives.sch" );
3913 size_t terminals = sheetControllerOffset( badPinHandle, 8 );
3914 writeU16( badPinHandle, terminals + 8 * 26, 0xFFFE );
3915 BOOST_CHECK_EXCEPTION( parser.
Parse( badPinHandle, wxS(
"bad-pin-handle.sch" ) ),
IO_ERROR,
3918 return aError.
What().Contains( wxS(
"pin-decal handle" ) )
3919 && aError.
What().Contains( wxS(
"controller 8" ) );
3922 std::vector<uint8_t> gateCount = loadBinaryFixture(
"multigate.sch" );
3923 size_t partTypes = sheetControllerOffset( gateCount, 9 );
3924 writeU16( gateCount, partTypes + 76 + 68, 3 );
3925 BOOST_CHECK_EXCEPTION( parser.
Parse( gateCount, wxS(
"gate-count.sch" ) ),
IO_ERROR,
3928 return aError.
What().Contains( wxS(
"gate count" ) )
3929 && aError.
What().Contains( wxS(
"controller 9" ) );
3932 std::vector<uint8_t> fieldHandle = loadBinaryFixture(
"symbol_primitives.sch" );
3933 size_t fieldDecals = sheetControllerOffset( fieldHandle, 7 );
3934 writeU32( fieldHandle, fieldDecals + 104 * 108 + 52, 132 );
3935 BOOST_CHECK_EXCEPTION( parser.
Parse( fieldHandle, wxS(
"field-handle.sch" ) ),
IO_ERROR,
3938 return aError.
What().Contains( wxS(
"field" ) )
3939 && aError.
What().Contains( wxS(
"controller 7" ) );
3942 std::vector<uint8_t> alternateHandle = loadBinaryFixture(
"multigate.sch" );
3943 size_t alternateGates = sheetControllerOffset( alternateHandle, 10 );
3944 writeU16( alternateHandle, alternateGates + 2, 0xFFFE );
3945 BOOST_CHECK_EXCEPTION( parser.
Parse( alternateHandle, wxS(
"alternate-handle.sch" ) ),
IO_ERROR,
3948 return aError.
What().Contains( wxS(
"alternate symbol definition" ) )
3949 && aError.
What().Contains( wxS(
"controller 10" ) );
3952 std::vector<uint8_t> binaryCycle = loadBinaryFixture(
"multigate.sch" );
3953 const size_t cyclePartTypes = sheetControllerOffset( binaryCycle, 9 );
3954 const size_t cycleGates = sheetControllerOffset( binaryCycle, 10 );
3955 const uint32_t firstGateRecord = readU32( binaryCycle, cyclePartTypes + 76 + 44 );
3956 const uint16_t firstDefinition = readU16( binaryCycle, cycleGates + firstGateRecord * 12 );
3957 const uint16_t secondDefinition = readU16( binaryCycle, cycleGates + ( firstGateRecord + 1 ) * 12 );
3958 writeU16( binaryCycle, cycleGates + firstGateRecord * 12 + 2, secondDefinition );
3959 writeU16( binaryCycle, cycleGates + ( firstGateRecord + 1 ) * 12 + 2, firstDefinition );
3960 BOOST_CHECK_EXCEPTION( parser.
Parse( binaryCycle, wxS(
"definition-cycle.sch" ) ),
IO_ERROR,
3963 return aError.
What().Contains( wxS(
"cyclic symbol definition reference" ) )
3964 && aError.
What().Contains( wxS(
"controller 10" ) );
3967 std::vector<uint8_t> unknownSide = loadBinaryFixture(
"pin_styles.sch" );
3968 const size_t unknownSideTerminals = sheetControllerOffset( unknownSide, 8 );
3969 writeU16( unknownSide, unknownSideTerminals + 22, 8 );
3971 BOOST_CHECK( std::ranges::any_of( unknownSideModel.
diagnostics,
3974 return aDiagnostic.message.Contains( wxS(
"terminal side" ) )
3975 && aDiagnostic.source.controller == 8
3976 && aDiagnostic.source.recordIndex == 0;
3979 std::vector<uint8_t> unknownLength = loadBinaryFixture(
"pin_styles.sch" );
3980 const PADS_SCH_MODEL unknownLengthBaseline = parser.
Parse( unknownLength, wxS(
"known-length.sch" ) );
3981 const size_t unknownLengthTerminalRecord =
static_cast<size_t>(
3982 itemNamed( unknownLengthBaseline.
definitions, wxS(
"BATCHB_PIN_STYLES" ) ).pins[0].source.recordIndex );
3983 const size_t unknownLengthTerminals = sheetControllerOffset( unknownLength, 8 );
3984 const uint16_t pinDecalHandle = readU16( unknownLength, unknownLengthTerminals + unknownLengthTerminalRecord * 26 );
3985 const size_t unknownLengthDecals = sheetControllerOffset( unknownLength, 7 );
3986 const uint32_t pinDecalDefinition = readU32( unknownLength, unknownLengthDecals + pinDecalHandle * 108 + 48 );
3987 const size_t unknownLengthDefinitions = sheetControllerOffset( unknownLength, 3 );
3988 const uint32_t pinDecalVertex = readU32( unknownLength, unknownLengthDefinitions + pinDecalDefinition * 80 + 0x34 );
3989 const uint32_t pinDecalVertexEnd =
3990 readU32( unknownLength, unknownLengthDefinitions + ( pinDecalDefinition + 1 ) * 80 + 0x34 );
3991 const size_t unknownLengthVertices = sheetControllerOffset( unknownLength, 5 );
3993 for( uint32_t vertex = pinDecalVertex; vertex < pinDecalVertexEnd; ++vertex )
3995 const size_t vertexOffset = unknownLengthVertices + vertex * 6;
3997 if( readU16( unknownLength, vertexOffset ) == 0 && readU16( unknownLength, vertexOffset + 2 ) == 0 )
3998 writeU16( unknownLength, vertexOffset, 1 );
4000 PADS_SCH_MODEL unknownLengthModel = parser.
Parse( unknownLength, wxS(
"unknown-length.sch" ) );
4002 itemNamed( unknownLengthModel.
definitions, wxS(
"BATCHB_PIN_STYLES" ) ).pins[0];
4004 BOOST_CHECK( std::ranges::any_of( unknownLengthPin.
properties,
4007 return aProperty.name.text == wxS(
"pin_length" )
4008 && aProperty.disposition == PROPERTY_DISPOSITION::UNSUPPORTED;
4011 std::vector<uint8_t> pieceCount = loadBinaryFixture(
"symbol_primitives.sch" );
4012 size_t definitions = sheetControllerOffset( pieceCount, 3 );
4013 pieceCount[definitions + 14 * 80 + 43] = 1;
4014 BOOST_CHECK_EXCEPTION( parser.
Parse( pieceCount, wxS(
"piece-count.sch" ) ),
IO_ERROR,
4017 return aError.
What().Contains( wxS(
"graphic-piece counts" ) )
4018 && aError.
What().Contains( wxS(
"controller 3" ) );
4021 std::vector<uint8_t> ownership = loadBinaryFixture(
"symbol_primitives.sch" );
4022 size_t ownershipDefinitions = sheetControllerOffset( ownership, 3 );
4023 const size_t vertexPrefix = ownershipDefinitions + 14 * 80 + 0x34;
4024 writeU32( ownership, vertexPrefix, readU32( ownership, vertexPrefix ) + 1 );
4025 BOOST_CHECK_EXCEPTION( parser.
Parse( ownership, wxS(
"piece-ownership.sch" ) ),
IO_ERROR,
4028 return aError.
What().Contains( wxS(
"ownership mismatch" ) )
4029 && aError.
What().Contains( wxS(
"controller 3" ) );
4032 auto duplicateModel = [&]()
4034 return parser.
Parse( loadBinaryFixture(
"multigate.sch" ), wxS(
"duplicate-id.sch" ) );
4041 return aError.What().Contains( wxS(
"duplicate definition ID" ) );
4048 return aError.What().Contains( wxS(
"duplicate part type ID" ) );
4055 return aError.What().Contains( wxS(
"duplicate gate ID" ) );
4059 size_t pinOwnerIndex = &pinOwner - duplicatePin.
definitions.data();
4064 return aError.What().Contains( wxS(
"duplicate pin ID" ) );
4073 return aError.What().Contains( wxS(
"duplicate field ID" ) )
4074 && aError.What().Contains( wxS(
"first at" ) );
4077 std::vector<uint8_t> duplicateBinaryField = loadBinaryFixture(
"fields.sch" );
4078 const size_t duplicateFieldDecals = sheetControllerOffset( duplicateBinaryField, 7 );
4079 const uint32_t textCount = sheetControllerCount( duplicateBinaryField, 1 );
4080 std::vector<size_t> fieldRecords;
4082 for(
size_t record = 0; record < sheetControllerCount( duplicateBinaryField, 7 ); ++record )
4084 if( readU32( duplicateBinaryField, duplicateFieldDecals + record * 108 + 52 ) < textCount )
4085 fieldRecords.push_back( record );
4088 BOOST_REQUIRE_GE( fieldRecords.size(), 2 );
4089 writeU32( duplicateBinaryField, duplicateFieldDecals + fieldRecords[1] * 108 + 52,
4090 readU32( duplicateBinaryField, duplicateFieldDecals + fieldRecords[0] * 108 + 52 ) );
4091 BOOST_CHECK_EXCEPTION( parser.
Parse( duplicateBinaryField, wxS(
"duplicate-field.sch" ) ),
IO_ERROR,
4094 return aError.
What().Contains( wxS(
"duplicate field ID" ) )
4095 && aError.
What().Contains( wxS(
"controller 7" ) )
4096 && aError.
What().Contains( wxS(
"first at" ) );
4099 std::vector<uint8_t> connectorPinName = loadBinaryFixture(
"connectors.sch" );
4100 const size_t connectorParts = sheetControllerOffset( connectorPinName, 9 );
4101 const size_t connectorPins = sheetControllerOffset( connectorPinName, 11 );
4102 const uint32_t connectorPinStart = readU32( connectorPinName, connectorParts + 76 + 48 );
4103 writeU32( connectorPinName, connectorPins + connectorPinStart * 24, 0xFFFFFFFE );
4104 BOOST_CHECK_EXCEPTION( parser.
Parse( connectorPinName, wxS(
"connector-pin-name.sch" ) ),
IO_ERROR,
4107 return aError.
What().Contains( wxS(
"pin-name offset leaves controller 14" ) )
4108 && aError.
What().Contains( wxS(
"controller 11" ) );
4116 PADS_SCH_MODEL binary = binaryParser.
Parse( loadBinaryFixture(
"pin_styles.sch" ), wxS(
"pin_styles.sch" ) );
4121 auto asciiDefinition = std::ranges::find_if( asciiParser.
GetSymbolDefs(),
4124 return aDefinition.name ==
"BATCHB_PIN_STYLES";
4130 static_cast<int64_t
>( std::llround( asciiDefinition->pins[0].position.x * 2 ) ) );
4132 ( asciiDefinition->pins[4].inverted ? 1U : 0U ) | ( asciiDefinition->pins[4].clock ? 2U : 0U ) );
4134 BOOST_REQUIRE_EQUAL( binaryDefinition.
graphics.size(), asciiDefinition->graphics.size() );
4136 for(
size_t i = 0; i < binaryDefinition.
graphics.size(); ++i )
4138 BOOST_CHECK( canonicalGraphicType( binaryDefinition.
graphics[i].kind )
4139 == canonicalGraphicType( asciiDefinition->graphics[i].type ) );
4141 std::llround( asciiDefinition->graphics[i].line_width * 2 ) );
4142 BOOST_CHECK( canonicalFill( binaryDefinition.
graphics[i].fill )
4143 == canonicalFill( asciiDefinition->graphics[i].filled ) );
4147 BOOST_REQUIRE_EQUAL( binaryDefinition.
pins.size(), asciiDefinition->pins.size() );
4149 BOOST_REQUIRE_EQUAL( asciiGate.
pins.size(), binaryDefinition.
pins.size() );
4153 BOOST_CHECK( canonicalPinStyle( aBinaryPin.
graphicStyle )
4154 == canonicalPinStyle( aAsciiPin.inverted, aAsciiPin.clock ) );
4178 aAsciiPin.pn_h != 0 && ( aAsciiPin.p_flags & 128 ) == 0 );
4182 for(
size_t i = 0; i < binaryDefinition.
pins.size(); ++i )
4186 BOOST_CHECK( canonicalPinType( binaryDefinition.
pins[i].electricalType )
4187 == canonicalPinType( asciiGate.
pins[i].pin_type ) );
4188 checkPinPresentation( binaryDefinition.
pins[i], asciiDefinition->pins[i] );
4192 binaryParser.
Parse( loadBinaryFixture(
"symbol_primitives.sch" ), wxS(
"symbol_primitives.sch" ) );
4197 itemNamed( primitiveBinary.
definitions, wxS(
"BATCHB_PRIMITIVES" ) );
4200 BOOST_REQUIRE_EQUAL( primitiveDefinition.
graphics.size(),
4201 asciiPrimitive->
graphics.size() + asciiPrimitive->
texts.size() );
4203 for(
size_t graphic = 0; graphic < asciiPrimitive->
graphics.size(); ++graphic )
4205 BOOST_CHECK( canonicalGraphicType( primitiveDefinition.
graphics[graphic].kind )
4206 == canonicalGraphicType( asciiPrimitive->
graphics[graphic] ) );
4208 std::llround( asciiPrimitive->
graphics[graphic].line_width * 2 ) );
4210 BOOST_CHECK( canonicalFill( primitiveDefinition.
graphics[graphic].fill )
4211 == canonicalFill( asciiPrimitive->
graphics[graphic].filled ) );
4212 BOOST_REQUIRE_EQUAL( primitiveDefinition.
graphics[graphic].points.size(),
4213 asciiPrimitive->
graphics[graphic].points.size() );
4215 for(
size_t pointIndex = 0; pointIndex < asciiPrimitive->
graphics[graphic].points.size(); ++pointIndex )
4218 std::llround( asciiPrimitive->
graphics[graphic].points[pointIndex].coord.x * 2 ) );
4220 std::llround( asciiPrimitive->
graphics[graphic].points[pointIndex].coord.y * 2 ) );
4225 BOOST_REQUIRE_EQUAL( asciiPrimitive->
texts.size(), 1 );
4235 == canonicalHorizontalJustification( asciiPrimitive->
texts[0].justification ).value );
4237 == canonicalVerticalJustification( asciiPrimitive->
texts[0].justification ).value );
4239 PADS_SCH_MODEL fieldsBinary = binaryParser.
Parse( loadBinaryFixture(
"fields.sch" ), wxS(
"fields.sch" ) );
4245 BOOST_REQUIRE_EQUAL( fieldsDefinition.
fields.size(), asciiFields->
attrs.size() );
4247 for(
size_t field = 0; field < asciiFields->
attrs.size(); ++field )
4252 wxString::FromUTF8( asciiFields->
attrs[field].attr_name ) );
4253 BOOST_CHECK( fieldsDefinition.
fields[field].value.text.empty() );
4255 std::llround( asciiFields->
attrs[field].position.x * 2 ) );
4257 std::llround( asciiFields->
attrs[field].position.y * 2 ) );
4262 wxString::FromUTF8( asciiFields->
attrs[field].font_name ) );
4264 canonicalJustification( fieldsDefinition.
fields[field].presentation.horizontalJustification ).value
4265 == canonicalHorizontalJustification( asciiFields->
attrs[field].justification ).value );
4267 canonicalVerticalJustification( fieldsDefinition.
fields[field].presentation.verticalJustification )
4269 == canonicalVerticalJustification( asciiFields->
attrs[field].justification ).value );
4274 BOOST_REQUIRE_EQUAL( fieldsPart.
fields.size(), fieldsDefinition.
fields.size() );
4276 for(
size_t field = 0; field < fieldsPart.
fields.size(); ++field )
4280 BOOST_CHECK( fieldsPart.
fields[field].presentation == fieldsDefinition.
fields[field].presentation );
4283 PADS_SCH_MODEL multiBinary = binaryParser.
Parse( loadBinaryFixture(
"multigate.sch" ), wxS(
"multigate.sch" ) );
4288 BOOST_REQUIRE_EQUAL( multiPart.
gates.size(), asciiMulti.
gates.size() );
4293 auto definition = std::ranges::find_if( aModel.
definitions,
4296 return aDefinition.id == aReference.id;
4299 return definition->name.text;
4307 auto pin = std::ranges::find_if( definition.
pins,
4310 return aPin.id == aReference.id;
4313 if(
pin != definition.
pins.end() )
4317 BOOST_FAIL(
"unresolved pin reference" );
4318 throw std::logic_error(
"unreachable" );
4321 for(
size_t gate = 0; gate < multiPart.
gates.size(); ++gate )
4324 wxString::FromUTF8( asciiMulti.
gates[gate].decal_names.front() ) );
4326 wxString::Format( wxS(
"%d" ), asciiMulti.
gates[gate].swap_flag ) );
4329 for(
size_t pin = 0;
pin < multiPart.
gates[gate].pins.size(); ++
pin )
4335 wxString::Format( wxS(
"%d" ), asciiMulti.
gates[gate].pins[
pin].swap_group ) );
4341 BOOST_REQUIRE_EQUAL( alternatePart.
gates[0].alternateDefinitions.size() + 1, asciiAlternates.
decal_names.size() );
4343 wxString::FromUTF8( asciiAlternates.
decal_names[0] ) );
4345 for(
size_t alternate = 0; alternate < alternatePart.
gates[0].alternateDefinitions.size(); ++alternate )
4347 BOOST_CHECK_EQUAL( definitionName( multiBinary, alternatePart.
gates[0].alternateDefinitions[alternate] ),
4348 wxString::FromUTF8( asciiAlternates.
decal_names[alternate + 1] ) );
4351 for(
size_t signal = 0; signal < multiPart.
signalPins.size(); ++signal )
4354 wxString::FromUTF8( asciiMulti.
sigpins[signal].pin_number ) );
4356 wxString::FromUTF8( asciiMulti.
sigpins[signal].net_name ) );
4360 binaryParser.
Parse( loadBinaryFixture(
"connectors.sch" ), wxS(
"connectors.sch" ) );
4365 BOOST_REQUIRE_EQUAL( connectorPart.
gates[0].decalGroupMembers.size(), asciiConnector.
decal_names.size() );
4367 for(
size_t member = 0; member < asciiConnector.
decal_names.size(); ++member )
4370 wxString::FromUTF8( asciiConnector.
decal_names[member] ) );
4373 const std::array<std::string, 11> pairedFixtures = {
"minimal_v13",
4374 "placement_transform",
4379 "connectivity_topology",
4380 "multisheet_connectivity",
4381 "symbol_primitives",
4385 for(
const std::string& fixture : pairedFixtures )
4388 binaryParser.
Parse( loadBinaryFixture( fixture +
".sch" ), wxString::FromUTF8( fixture ) );
4395 if( binarySymbol.
pins.empty() )
4399 BOOST_REQUIRE_MESSAGE( asciiSymbol, fixture +
": " + binarySymbol.
name.
text.ToStdString() );
4400 BOOST_REQUIRE_EQUAL( binarySymbol.
pins.size(), asciiSymbol->
pins.size() );
4402 for(
size_t pin = 0;
pin < binarySymbol.
pins.size(); ++
pin )
4405 << binarySymbol.
pins[
pin].presentationFlags <<
"/"
4406 << binarySymbol.
pins[
pin].visibilityAndNumberPresentationFlags )
4408 checkPinPresentation( binarySymbol.
pins[
pin], asciiSymbol->
pins[
pin] );
4416 BOOST_REQUIRE_MESSAGE( asciiPart != fixtureAscii.
GetPartTypes().end(),
4417 fixture +
": " + binaryPart.
name.
text.ToStdString() );
4419 if( asciiPart->second.gates.empty() )
4422 BOOST_REQUIRE_EQUAL( binaryPart.
gates.size(), asciiPart->second.gates.size() );
4424 for(
size_t gate = 0; gate < binaryPart.
gates.size(); ++gate )
4431 BOOST_REQUIRE_EQUAL( binaryGate.
connectorPins.size(), asciiGateForPart.
pins.size() );
4439 wxString::FromUTF8( asciiGateForPart.
pins[
pin].pin_id ) );
4441 wxString::FromUTF8( asciiGateForPart.
pins[
pin].pin_name ) );
4443 == canonicalPinType( asciiGateForPart.
pins[
pin].pin_type ) );
4450 BOOST_REQUIRE_EQUAL( binaryGate.
pins.size(), asciiGateForPart.
pins.size() );
4451 BOOST_REQUIRE_EQUAL( binaryGate.
logicalPins.size(), asciiGateForPart.
pins.size() );
4459 wxString::FromUTF8( asciiGateForPart.
pins[
pin].pin_id ) );
4461 wxString::FromUTF8( asciiGateForPart.
pins[
pin].pin_name ) );
4463 == canonicalPinType( asciiGateForPart.
pins[
pin].pin_type ) );
4474 static constexpr std::array<const char*, 6> fixtures = {
"terminal_justification_0_6",
4475 "terminal_justification_7_13",
4476 "terminal_justification_14_15",
4477 "terminal_justification_90_0_6",
4478 "terminal_justification_90_7_13",
4479 "terminal_justification_90_14_15" };
4483 for(
const char* fixture : fixtures )
4485 const std::string binaryName = std::string( fixture ) +
".sch";
4486 const std::string asciiName = std::string( fixture ) +
".txt";
4487 PADS_SCH_MODEL binary = binaryParser.
Parse( loadBinaryFixture( binaryName ), wxString::FromUTF8( binaryName ) );
4494 BOOST_REQUIRE_EQUAL( binaryDefinition.
pins.size(), asciiDefinition->
pins.size() );
4496 for(
size_t pin = 0;
pin < binaryDefinition.
pins.size(); ++
pin )
4499 asciiDefinition->
pins[
pin].pn_angle * 10 );
4501 asciiDefinition->
pins[
pin].pl_angle * 10 );
4515 BOOST_REQUIRE_EQUAL(
model.texts.size(), 1 );
4516 const size_t justificationOffset =
model.texts.front().source.absoluteOffset + 18;
4518 for( uint16_t code : { 0, 1, 2, 3, 4, 6, 8, 9, 12 } )
4522 std::vector<uint8_t> bytes = loadBinaryFixture(
"text_encoding.sch" );
4523 writeU16( bytes, justificationOffset, code );
4526 BOOST_REQUIRE_EQUAL( patched.
texts.size(), 1 );
4537 BOOST_CHECK( vertical ==
expected );
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Parser for PADS Logic ASCII schematic export files.
const std::vector< TEXT_ITEM > & GetTextItems() const
const std::vector< TIED_DOT > & GetTiedDots() const
const std::vector< OFF_PAGE_CONNECTOR > & GetOffPageConnectors() const
bool Parse(const std::string &aFileName)
const std::vector< SHEET_HEADER > & GetSheetHeaders() const
const std::vector< NETNAME_LABEL > & GetNetNameLabels() const
const std::vector< BUS_DEF > & GetBuses() const
const std::vector< SCH_SIGNAL > & GetSignals() const
const std::vector< LINES_ITEM > & GetLinesItems() const
const std::vector< SYMBOL_DEF > & GetSymbolDefs() const
const std::vector< PART_PLACEMENT > & GetPartPlacements() const
const std::map< std::string, PARTTYPE_DEF > & GetPartTypes() const
const SYMBOL_DEF * GetSymbolDef(const std::string &aName) const
const PARAMETERS & GetParameters() const
constexpr ValueType Value() const
constexpr bool IsValid() const
PADS_SCH_MODEL Parse(const std::vector< uint8_t > &aBytes, const wxString &aSourceName={}) const
static SOURCE_STRING DecodeString(const std::vector< uint8_t > &aBytes, uint32_t aCodePage, const SOURCE_PROVENANCE &aSource, std::vector< PARSER_DIAGNOSTIC > &aDiagnostics, const wxString &aRecordedCodePageName={})
static void RecordUnknownEnum(const wxString &aEnumName, uint32_t aValue, const SOURCE_PROVENANCE &aSource, std::vector< PARSER_DIAGNOSTIC > &aDiagnostics)
void Load(std::vector< uint8_t > aBytes)
const std::vector< SCH_SDB_OLE_ITEM > & OleItems() const
static bool empty(const wxTextEntryBase *aCtrl)
static std::string ToStdString(const wxString &aStr)
@ BUS
the source net is carried by a bus
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
void DecodeJustification(int aJustification, GR_TEXT_H_ALIGN_T &aHJustify, GR_TEXT_V_ALIGN_T &aVJustify)
Decode a PADS text justification code into KiCad horizontal and vertical alignment.
constexpr int64_t NormalizeCoordinate(int64_t aCoordinate)
CONTROLLER_REFERENCE< DEFINITION_ID > DEFINITION_REFERENCE
constexpr int NormalizeAngle(int aAngle)
CONTROLLER_ID< NET_ID_TAG > NET_ID
CONTROLLER_ID< DEFINITION_ID_TAG > DEFINITION_ID
CONTROLLER_REFERENCE< PLACEMENT_ID > PLACEMENT_REFERENCE
CONTROLLER_REFERENCE< SHEET_ID > SHEET_REFERENCE
CONTROLLER_ID< SHEET_ID_TAG > SHEET_ID
CONTROLLER_ID< PIN_ID_TAG > PIN_ID
CONTROLLER_ID< FIELD_ID_TAG, uint64_t > FIELD_ID
constexpr FIELD_ID MakeFieldId(FIELD_ID_DOMAIN aDomain, uint32_t aOwner, uint32_t aOrdinal)
CONTROLLER_ID< PART_TYPE_ID_TAG > PART_TYPE_ID
wxString FormatParserError(const SOURCE_PROVENANCE &aSource, const wxString &aMessage)
CONTROLLER_ID< PLACEMENT_ID_TAG > PLACEMENT_ID
CONTROLLER_ID< GATE_ID_TAG > GATE_ID
CONTROLLER_REFERENCE< NET_ID > NET_REFERENCE
CONTROLLER_REFERENCE< GATE_ID > GATE_REFERENCE
CONTROLLER_ID< BUS_ID_TAG > BUS_ID
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ NET
This item represents a net.
std::vector< std::string > decal_names
std::vector< PARTTYPE_PIN > pins
std::map< std::string, std::string > fields
std::vector< GATE_DEF > gates
std::vector< SIGPIN > sigpins
std::string decal_name
CAEDECAL the placed gate draws, empty when unresolved.
std::vector< PART_ATTRIBUTE > attributes
std::vector< PIN_OVERRIDE > pin_overrides
std::vector< SYMBOL_GRAPHIC > graphics
std::vector< SYMBOL_PIN > pins
std::vector< CAEDECAL_ATTR > attrs
std::vector< SYMBOL_TEXT > texts
std::vector< GRAPHIC_POINT > points
int64_t coordinateUnitsPerMil
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_BUS_ENTRY > entries
std::vector< SOURCE_STRING > aliases
std::vector< NET_REFERENCE > memberNets
std::vector< SOURCE_POINT > vertices
std::vector< MODEL_CONNECTION_ENDPOINT > endpoints
std::vector< SOURCE_POINT > vertices
MODEL_TEXT_PRESENTATION presentation
std::vector< SOURCE_PROPERTY > properties
std::vector< DEFINITION_REFERENCE > decalGroupMembers
std::vector< MODEL_CONNECTOR_PIN > connectorPins
std::vector< PIN_REFERENCE > pins
std::vector< MODEL_GATE_PIN > logicalPins
std::vector< SOURCE_PROPERTY > properties
MODEL_TEXT_PRESENTATION presentation
SOURCE_POINT arcBoundsStart
SOURCE_POINT arcBoundsEnd
MODEL_LINE_STYLE lineStyle
std::vector< SOURCE_POINT > points
std::vector< MODEL_CONNECTION > connections
std::vector< MODEL_SIGNAL_PIN > signalPins
std::vector< MODEL_FIELD > fields
std::vector< MODEL_GATE > gates
uint16_t numberOffsetJustification
MODEL_TEXT_PRESENTATION namePresentation
std::vector< SOURCE_PROPERTY > properties
uint16_t nameOffsetJustification
SOURCE_POINT numberOffset
uint16_t numberJustification
uint16_t visibilityAndNumberPresentationFlags
MODEL_TEXT_PRESENTATION numberPresentation
uint16_t nameJustification
PART_TYPE_REFERENCE partType
DEFINITION_REFERENCE definition
std::vector< PLACED_PIN_REFERENCE > pins
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_FIELD > fields
std::optional< GATE_REFERENCE > gate
std::optional< SHEET_REFERENCE > parent
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_FIELD > titleBlockFields
std::vector< MODEL_GRAPHIC > border
std::vector< MODEL_GRAPHIC > graphics
std::vector< MODEL_PIN_DEFINITION > pins
std::vector< MODEL_FIELD > fields
MODEL_JUSTIFICATION verticalJustification
MODEL_JUSTIFICATION horizontalJustification
std::vector< MODEL_GRAPHIC > graphics
std::vector< MODEL_NET > nets
std::vector< MODEL_SYMBOL_DEFINITION > definitions
std::vector< MODEL_JUNCTION > junctions
void ValidateOrThrow() const
std::vector< MODEL_PAGE_GRAPHIC > graphics
std::vector< MODEL_WORKSHEET > worksheets
std::vector< MODEL_BUS > buses
std::vector< MODEL_LABEL > labels
std::vector< MODEL_EMBEDDED_IMAGE > images
std::vector< MODEL_TEXT > texts
std::vector< MODEL_PART_TYPE > partTypes
std::vector< MODEL_SHEET > sheets
std::vector< MODEL_PLACEMENT > placements
std::vector< PARSER_DIAGNOSTIC > diagnostics
STRING_ENCODING_STATUS encoding
std::vector< uint8_t > raw
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_CHECK_EQUAL_COLLECTIONS(mixed.begin(), mixed.end(), expMixed.begin(), expMixed.end())
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
BOOST_AUTO_TEST_CASE(EmbeddedOleImages)
BOOST_TEST_CONTEXT("Test Clearance")
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.