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.

554 lines
18 KiB

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