You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

558 lines
18 KiB

17 years ago
14 years ago
14 years ago
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2007-2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2007 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /* This source is a complement to specctra.cpp and implements the import of
  25. a specctra session file (*.ses), and import of a specctra design file
  26. (*.dsn) file. The specification for the grammar of the specctra dsn file
  27. used to develop this code is given here:
  28. http://tech.groups.yahoo.com/group/kicad-users/files/ then file "specctra.pdf"
  29. Also see the comments at the top of the specctra.cpp file itself.
  30. */
  31. #include "class_drawpanel.h" // m_canvas
  32. #include "confirm.h" // DisplayError()
  33. #include "gestfich.h" // EDA_FileSelector()
  34. #include "wxPcbStruct.h"
  35. #include "class_board.h"
  36. #include "class_module.h"
  37. #include "class_edge_mod.h"
  38. #include "class_track.h"
  39. #include "class_zone.h"
  40. #include "class_drawsegment.h"
  41. #include "specctra.h"
  42. using namespace DSN;
  43. void PCB_EDIT_FRAME::ImportSpecctraDesign( wxCommandEvent& event )
  44. {
  45. /* @todo write this someday
  46. if( !Clear_Pcb( true ) )
  47. return;
  48. */
  49. }
  50. void PCB_EDIT_FRAME::ImportSpecctraSession( wxCommandEvent& event )
  51. {
  52. /*
  53. if( GetScreen()->IsModify() )
  54. {
  55. if( !IsOK( this, _( "Board Modified: Continue ?" ) ) )
  56. return;
  57. }
  58. */
  59. wxString fullFileName = GetScreen()->GetFileName();
  60. wxString path;
  61. wxString name;
  62. wxString ext;
  63. wxString sessionExt( wxT( ".ses" ) );
  64. wxString mask = wxT( "*" ) + sessionExt;
  65. wxFileName::SplitPath( fullFileName, &path, &name, &ext );
  66. name += sessionExt;
  67. fullFileName = EDA_FileSelector( _( "Merge Specctra Session file:" ),
  68. path,
  69. name,
  70. sessionExt,
  71. mask,
  72. this,
  73. wxFD_OPEN,
  74. false
  75. );
  76. if( fullFileName == wxEmptyString )
  77. return;
  78. SPECCTRA_DB db;
  79. SetLocaleTo_C_standard( ); // Switch the locale to standard C
  80. try
  81. {
  82. db.LoadSESSION( fullFileName );
  83. db.FromSESSION( GetBoard() );
  84. }
  85. catch( IO_ERROR& ioe )
  86. {
  87. SetLocaleTo_Default( ); // revert to the current locale
  88. ioe.errorText += '\n';
  89. ioe.errorText += _("BOARD may be corrupted, do not save it.");
  90. ioe.errorText += '\n';
  91. ioe.errorText += _("Fix problem and try again.");
  92. DisplayError( this, ioe.errorText );
  93. return;
  94. }
  95. SetLocaleTo_Default( ); // revert to the current locale
  96. OnModify();
  97. GetBoard()->m_Status_Pcb = 0;
  98. /* At this point we should call Compile_Ratsnest()
  99. * but this could be time consumming.
  100. * So if incorrect number of Connecred and No connected pads is accepted
  101. * until Compile_Ratsnest() is called (when track tool selected for instance)
  102. * leave the next line commented
  103. * Otherwise uncomment this line
  104. */
  105. //Compile_Ratsnest( NULL, true );
  106. SetStatusText( wxString( _( "Session file imported and merged OK." ) ) );
  107. m_canvas->Refresh( true );
  108. }
  109. namespace DSN {
  110. /**
  111. * Function scale
  112. * converts a session file distance to KiCad units of deci-mils.
  113. * @param distance The session file length to convert.
  114. * @param aResolution The session UNIT_RES which holds the engineering unit
  115. * specifier
  116. * @return int - The KiCad length in deci-mils
  117. */
  118. static int scale( double distance, UNIT_RES* aResolution )
  119. {
  120. double resValue = aResolution->GetValue();
  121. double factor; // multiply this times session value to get mils for KiCad.
  122. switch( aResolution->GetEngUnits() )
  123. {
  124. default:
  125. case T_inch:
  126. factor = 1000.0;
  127. break;
  128. case T_mil:
  129. factor = 1.0;
  130. break;
  131. case T_cm:
  132. factor = 1000.0/2.54;
  133. break;
  134. case T_mm:
  135. factor = 1000.0/25.4;
  136. break;
  137. case T_um:
  138. factor = 1.0/25.4;
  139. break;
  140. }
  141. // the factor of 10.0 is used to convert mils to deci-mils, the units
  142. // used within KiCad.
  143. factor *= 10.0;
  144. int ret = wxRound( factor * distance / resValue );
  145. return ret;
  146. }
  147. /**
  148. * Function mapPt
  149. * translates a point from the Specctra Session format coordinate system
  150. * to the KiCad coordinate system.
  151. * @param aPoint The session point to translate
  152. * @param aResolution - The amount to scale the point.
  153. * @return wxPoint - The KiCad coordinate system point.
  154. */
  155. static wxPoint mapPt( const POINT& aPoint, UNIT_RES* aResolution )
  156. {
  157. wxPoint ret( scale( aPoint.x, aResolution ),
  158. -scale( aPoint.y, aResolution ) ); // negate y
  159. return ret;
  160. }
  161. TRACK* SPECCTRA_DB::makeTRACK( PATH* aPath, int aPointIndex, int aNetcode ) throw( IO_ERROR )
  162. {
  163. int layerNdx = findLayerName( aPath->layer_id );
  164. if( layerNdx == -1 )
  165. {
  166. wxString layerName = FROM_UTF8( aPath->layer_id.c_str() );
  167. ThrowIOError( _("Session file uses invalid layer id \"%s\""),
  168. GetChars(layerName) );
  169. }
  170. TRACK* track = new TRACK( sessionBoard );
  171. track->m_Start = mapPt( aPath->points[aPointIndex+0], routeResolution );
  172. track->m_End = mapPt( aPath->points[aPointIndex+1], routeResolution );
  173. track->SetLayer( pcbLayer2kicad[layerNdx] );
  174. track->m_Width = scale( aPath->aperture_width, routeResolution );
  175. track->SetNet( aNetcode );
  176. return track;
  177. }
  178. SEGVIA* SPECCTRA_DB::makeVIA( PADSTACK* aPadstack, const POINT& aPoint, int aNetCode ) throw( IO_ERROR )
  179. {
  180. SEGVIA* via = 0;
  181. SHAPE* shape;
  182. int shapeCount = aPadstack->Length();
  183. int drillDiam = -1;
  184. int copperLayerCount = sessionBoard->GetCopperLayerCount();
  185. // The drill diameter is encoded in the padstack name if Pcbnew did the DSN export.
  186. // It is in mils and is after the colon and before the last '_'
  187. int drillStartNdx = aPadstack->padstack_id.find( ':' );
  188. if( drillStartNdx != -1 )
  189. {
  190. ++drillStartNdx; // skip over the ':'
  191. int drillEndNdx = aPadstack->padstack_id.rfind( '_' );
  192. if( drillEndNdx != -1 )
  193. {
  194. std::string diamTxt( aPadstack->padstack_id, drillStartNdx, drillEndNdx-drillStartNdx );
  195. const char* sdiamTxt = diamTxt.c_str();
  196. double drillMils = strtod( sdiamTxt, 0 );
  197. // drillMils is not in the session units, but actual mils so we don't use scale()
  198. drillDiam = (int) (drillMils * 10);
  199. /** @todo: see if we use default netclass or specific value
  200. */
  201. drillDiam = -1; // import as default: real drill is the netclass value
  202. }
  203. }
  204. if( shapeCount == 0 )
  205. {
  206. ThrowIOError( _( "Session via padstack has no shapes") );
  207. }
  208. else if( shapeCount == 1 )
  209. {
  210. shape = (SHAPE*) (*aPadstack)[0];
  211. DSN_T type = shape->shape->Type();
  212. if( type != T_circle )
  213. ThrowIOError( _( "Unsupported via shape: %s"),
  214. GetChars( GetTokenString( type ) ) );
  215. CIRCLE* circle = (CIRCLE*) shape->shape;
  216. int viaDiam = scale( circle->diameter, routeResolution );
  217. via = new SEGVIA( sessionBoard );
  218. via->SetPosition( mapPt( aPoint, routeResolution ) );
  219. via->SetDrill( drillDiam );
  220. via->m_Shape = VIA_THROUGH;
  221. via->m_Width = viaDiam;
  222. via->SetLayerPair( LAYER_N_FRONT, LAYER_N_BACK );
  223. }
  224. else if( shapeCount == copperLayerCount )
  225. {
  226. shape = (SHAPE*) (*aPadstack)[0];
  227. DSN_T type = shape->shape->Type();
  228. if( type != T_circle )
  229. ThrowIOError( _( "Unsupported via shape: %s"),
  230. GetChars( GetTokenString( type ) ) );
  231. CIRCLE* circle = (CIRCLE*) shape->shape;
  232. int viaDiam = scale( circle->diameter, routeResolution );
  233. via = new SEGVIA( sessionBoard );
  234. via->SetPosition( mapPt( aPoint, routeResolution ) );
  235. via->SetDrill( drillDiam );
  236. via->m_Shape = VIA_THROUGH;
  237. via->m_Width = viaDiam;
  238. via->SetLayerPair( LAYER_N_FRONT, LAYER_N_BACK );
  239. }
  240. else // VIA_MICROVIA or VIA_BLIND_BURIED
  241. {
  242. int topLayerNdx = -1;
  243. int botLayerNdx = 7000;
  244. int viaDiam = -1;
  245. for( int i=0; i<shapeCount; ++i )
  246. {
  247. shape = (SHAPE*) (*aPadstack)[i];
  248. DSN_T type = shape->shape->Type();
  249. if( type != T_circle )
  250. ThrowIOError( _( "Unsupported via shape: %s"),
  251. GetChars( GetTokenString( type ) ) );
  252. CIRCLE* circle = (CIRCLE*) shape->shape;
  253. int layerNdx = findLayerName( circle->layer_id );
  254. if( layerNdx == -1 )
  255. {
  256. wxString layerName = FROM_UTF8( circle->layer_id.c_str() );
  257. ThrowIOError( _("Session file uses invalid layer id \"%s\""),
  258. GetChars( layerName ) );
  259. }
  260. if( layerNdx > topLayerNdx )
  261. topLayerNdx = layerNdx;
  262. if( layerNdx < botLayerNdx )
  263. botLayerNdx = layerNdx;
  264. if( viaDiam == -1 )
  265. viaDiam = scale( circle->diameter, routeResolution );
  266. }
  267. via = new SEGVIA( sessionBoard );
  268. via->SetPosition( mapPt( aPoint, routeResolution ) );
  269. via->SetDrill( drillDiam );
  270. if( (topLayerNdx==0 && botLayerNdx==1)
  271. || (topLayerNdx==copperLayerCount-2 && botLayerNdx==copperLayerCount-1))
  272. via->m_Shape = VIA_MICROVIA;
  273. else
  274. via->m_Shape = VIA_BLIND_BURIED;
  275. via->m_Width = viaDiam;
  276. topLayerNdx = pcbLayer2kicad[topLayerNdx];
  277. botLayerNdx = pcbLayer2kicad[botLayerNdx];
  278. via->SetLayerPair( topLayerNdx, botLayerNdx );
  279. }
  280. if( via )
  281. via->SetNet( aNetCode );
  282. return via;
  283. }
  284. // no UI code in this function, throw exception to report problems to the
  285. // UI handler: void PCB_EDIT_FRAME::ImportSpecctraSession( wxCommandEvent& event )
  286. void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IO_ERROR )
  287. {
  288. sessionBoard = aBoard; // not owned here
  289. if( !session )
  290. ThrowIOError( _("Session file is missing the \"session\" section") );
  291. if( !session->placement )
  292. ThrowIOError( _("Session file is missing the \"placement\" section") );
  293. if( !session->route )
  294. ThrowIOError( _("Session file is missing the \"routes\" section") );
  295. if( !session->route->library )
  296. ThrowIOError( _("Session file is missing the \"library_out\" section") );
  297. // delete all the old tracks and vias
  298. aBoard->m_Track.DeleteAll();
  299. aBoard->DeleteMARKERs();
  300. buildLayerMaps( aBoard );
  301. #if 1
  302. // Walk the PLACEMENT object's COMPONENTs list, and for each PLACE within
  303. // each COMPONENT, reposition and re-orient each component and put on
  304. // correct side of the board.
  305. COMPONENTS& components = session->placement->components;
  306. for( COMPONENTS::iterator comp=components.begin(); comp!=components.end(); ++comp )
  307. {
  308. PLACES& places = comp->places;
  309. for( unsigned i=0; i<places.size(); ++i )
  310. {
  311. PLACE* place = &places[i]; // '&' even though places[] holds a pointer!
  312. wxString reference = FROM_UTF8( place->component_id.c_str() );
  313. MODULE* module = aBoard->FindModuleByReference( reference );
  314. if( !module )
  315. {
  316. ThrowIOError(
  317. _("Session file has 'reference' to non-existent component \"%s\""),
  318. GetChars( reference ) );
  319. }
  320. if( !place->hasVertex )
  321. continue;
  322. UNIT_RES* resolution = place->GetUnits();
  323. wxASSERT( resolution );
  324. wxPoint newPos = mapPt( place->vertex, resolution );
  325. module->SetPosition( newPos );
  326. if( place->side == T_front )
  327. {
  328. // convert from degrees to tenths of degrees used in KiCad.
  329. int orientation = (int) (place->rotation * 10.0);
  330. if( module->GetLayer() != LAYER_N_FRONT )
  331. {
  332. // module is on copper layer (back)
  333. module->Flip( module->m_Pos );
  334. }
  335. module->SetOrientation( orientation );
  336. }
  337. else if( place->side == T_back )
  338. {
  339. int orientation = (int) ((place->rotation + 180.0) * 10.0);
  340. if( module->GetLayer() != LAYER_N_BACK )
  341. {
  342. // module is on component layer (front)
  343. module->Flip( module->m_Pos );
  344. }
  345. module->SetOrientation( orientation );
  346. }
  347. else
  348. {
  349. // as I write this, the PARSER *is* catching this, so we should never see below:
  350. wxFAIL_MSG( wxT("DSN::PARSER did not catch an illegal side := 'back|front'") );
  351. }
  352. }
  353. }
  354. #endif
  355. routeResolution = session->route->GetUnits();
  356. // Walk the NET_OUTs and create tracks and vias anew.
  357. NET_OUTS& net_outs = session->route->net_outs;
  358. for( NET_OUTS::iterator net=net_outs.begin(); net!=net_outs.end(); ++net )
  359. {
  360. int netCode = 0;
  361. // page 143 of spec says wire's net_id is optional
  362. if( net->net_id.size() )
  363. {
  364. wxString netName = FROM_UTF8( net->net_id.c_str() );
  365. NETINFO_ITEM* net = aBoard->FindNet( netName );
  366. if( net )
  367. netCode = net->GetNet();
  368. else // else netCode remains 0
  369. {
  370. // int breakhere = 1;
  371. }
  372. }
  373. WIRES& wires = net->wires;
  374. for( unsigned i=0; i<wires.size(); ++i )
  375. {
  376. WIRE* wire = &wires[i];
  377. DSN_T shape = wire->shape->Type();
  378. if( shape != T_path )
  379. {
  380. /* shape == T_polygon is expected from freerouter if you have
  381. a zone on a non "power" type layer, i.e. a T_signal layer
  382. and the design does a round trip back in as session here.
  383. We kept our own zones in the BOARD, so ignore this so called
  384. 'wire'.
  385. wxString netId = FROM_UTF8( wire->net_id.c_str() );
  386. ThrowIOError(
  387. _("Unsupported wire shape: \"%s\" for net: \"%s\""),
  388. DLEX::GetTokenString(shape).GetData(),
  389. netId.GetData()
  390. );
  391. */
  392. }
  393. else
  394. {
  395. PATH* path = (PATH*) wire->shape;
  396. for( unsigned pt=0; pt<path->points.size()-1; ++pt )
  397. {
  398. /* a debugging aid, may come in handy
  399. if( path->points[pt].x == 547800
  400. && path->points[pt].y == -380250 )
  401. {
  402. int breakhere = 1;
  403. }
  404. */
  405. TRACK* track = makeTRACK( path, pt, netCode );
  406. aBoard->Add( track );
  407. }
  408. }
  409. }
  410. WIRE_VIAS& wire_vias = net->wire_vias;
  411. LIBRARY& library = *session->route->library;
  412. for( unsigned i=0; i<wire_vias.size(); ++i )
  413. {
  414. int netCode = 0;
  415. // page 144 of spec says wire_via's net_id is optional
  416. if( net->net_id.size() )
  417. {
  418. wxString netName = FROM_UTF8( net->net_id.c_str() );
  419. NETINFO_ITEM* net = aBoard->FindNet( netName );
  420. if( net )
  421. netCode = net->GetNet();
  422. // else netCode remains 0
  423. }
  424. WIRE_VIA* wire_via = &wire_vias[i];
  425. // example: (via Via_15:8_mil 149000 -71000 )
  426. PADSTACK* padstack = library.FindPADSTACK( wire_via->GetPadstackId() );
  427. if( !padstack )
  428. {
  429. // Dick Feb 29, 2008:
  430. // Freerouter has a bug where it will not round trip all vias.
  431. // Vias which have a (use_via) element will be round tripped.
  432. // Vias which do not, don't come back in in the session library,
  433. // even though they may be actually used in the pre-routed,
  434. // protected wire_vias. So until that is fixed, create the
  435. // padstack from its name as a work around.
  436. // Could use a STRING_FORMATTER here and convert the entire
  437. // wire_via to text and put that text into the exception.
  438. wxString psid( FROM_UTF8( wire_via->GetPadstackId().c_str() ) );
  439. ThrowIOError( _("A wire_via references a missing padstack \"%s\""),
  440. GetChars( psid ) );
  441. }
  442. for( unsigned v=0; v<wire_via->vertexes.size(); ++v )
  443. {
  444. SEGVIA* via = makeVIA( padstack, wire_via->vertexes[v], netCode );
  445. aBoard->Add( via );
  446. }
  447. }
  448. }
  449. }
  450. } // namespace DSN