libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmDspPgmJsonToDot.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmFloatTypes.h"
  6. #include "cmComplexTypes.h"
  7. #include "cmRpt.h"
  8. #include "cmErr.h"
  9. #include "cmCtx.h"
  10. #include "cmMem.h"
  11. #include "cmMallocDebug.h"
  12. #include "cmLinkedHeap.h"
  13. #include "cmSymTbl.h"
  14. #include "cmJson.h"
  15. #include "cmText.h"
  16. #include "cmDspPgmJsonToDot.h"
  17. #include "cmFile.h"
  18. #include "cmFileSys.h"
  19. struct cmDotProc_str;
  20. typedef struct cmDotPort_str
  21. {
  22. struct cmDotProc_str* proc;
  23. cmChar_t* labelStr;
  24. unsigned portNo;
  25. unsigned connCnt; // count of connections to this port
  26. struct cmDotPort_str* link;
  27. } cmDotPort_t;
  28. typedef struct cmDotProc_str
  29. {
  30. cmChar_t* classStr;
  31. cmChar_t* instStr;
  32. cmChar_t* outStr;
  33. unsigned portCnt;
  34. bool skipFl;
  35. cmDotPort_t* ports;
  36. struct cmDotProc_str* link;
  37. } cmDotProc_t;
  38. typedef struct cmDotConn_str
  39. {
  40. cmDotPort_t* srcPort; // output
  41. cmDotPort_t* dstPort; // input
  42. bool skipFl;
  43. struct cmDotConn_str* link;
  44. } cmDotConn_t;
  45. typedef struct cmDot_str
  46. {
  47. cmErr_t err;
  48. cmLHeapH_t lH;
  49. cmDotProc_t* procs;
  50. cmDotConn_t* conns;
  51. } cmDot_t;
  52. typedef struct
  53. {
  54. const cmChar_t* s0;
  55. const cmChar_t* s1;
  56. } cmDotSubst_t;
  57. cmDotSubst_t _cmDotSubstArray[] =
  58. {
  59. { "Router", "Rtr" },
  60. { "Scalar", "Sc" },
  61. { "ScaleRange", "SR" },
  62. { "MsgList", "ML" },
  63. { "Button", "Btn" },
  64. { "PortToSym", "PtS" },
  65. { "1ofN", "lOfN"},
  66. { NULL, NULL }
  67. };
  68. const cmChar_t* _cmDotSkipClassArray[] =
  69. {
  70. "Scalar",
  71. NULL
  72. };
  73. void _cmDotReplaceDash( cmChar_t* str )
  74. {
  75. cmChar_t* s = str;
  76. for(; *s; ++s )
  77. if( *s == '-' )
  78. *s = '_';
  79. }
  80. cmChar_t* _cmDotSubstitute( cmDot_t* p, const cmChar_t* label )
  81. {
  82. unsigned i;
  83. cmChar_t* s = cmLhAllocStr(p->lH,label);
  84. for(i=0; _cmDotSubstArray[i].s0 != NULL; ++i)
  85. {
  86. unsigned n0 = cmTextLength(_cmDotSubstArray[i].s0);
  87. if( cmTextCmpN( _cmDotSubstArray[i].s0, s, n0 ) == 0 )
  88. {
  89. unsigned n1 = cmTextLength(_cmDotSubstArray[i].s1);
  90. assert(n0>=n1);
  91. cmTextShrinkS(s,s+n1,n0-n1);
  92. strncpy(s,_cmDotSubstArray[i].s1,n1);
  93. }
  94. }
  95. return s;
  96. }
  97. bool _cmDotIsSkipClass( const cmChar_t* classStr )
  98. {
  99. unsigned i;
  100. for(i=0; _cmDotSkipClassArray[i]!=NULL; ++i)
  101. if( cmTextCmp(_cmDotSkipClassArray[i],classStr) == 0 )
  102. return true;
  103. return false;
  104. }
  105. cmDotPort_t* _cmDotFindPort( cmDotProc_t* proc, const cmChar_t* labelStr )
  106. {
  107. cmDotPort_t* port = proc->ports;
  108. for(; port!=NULL; port=port->link)
  109. if( cmTextCmp(port->labelStr,labelStr) == 0 )
  110. return port;
  111. return NULL;
  112. }
  113. cmDotProc_t* _cmDotFindProc( cmDot_t* p, const cmChar_t* instStr )
  114. {
  115. cmDotProc_t* dp = p->procs;
  116. for(; dp!=NULL; dp=dp->link)
  117. if( cmTextCmp(dp->instStr,instStr) == 0 )
  118. return dp;
  119. return NULL;
  120. }
  121. cmDotPort_t* _cmDotNewPort( cmDot_t* p, cmDotProc_t* proc, const cmChar_t* labelStr )
  122. {
  123. cmDotPort_t* port = NULL;
  124. if( labelStr==NULL || cmTextLength(labelStr)==0 )
  125. {
  126. cmErrMsg(&p->err,kInvalidArgDotRC,"A blank port label was encountered.");
  127. return NULL;
  128. }
  129. if((port = _cmDotFindPort(proc,labelStr)) != NULL )
  130. return port;
  131. port = cmLhAllocZ(p->lH,cmDotPort_t,1);
  132. port->proc = proc;
  133. port->labelStr = cmLhAllocStr(p->lH,labelStr);
  134. port->portNo = proc->portCnt;
  135. proc->portCnt += 1;
  136. cmDotPort_t* p0 = NULL;
  137. cmDotPort_t* p1 = proc->ports;
  138. for(; p1!=NULL; p1=p1->link)
  139. p0 = p1;
  140. if( p0 == NULL )
  141. proc->ports = port;
  142. else
  143. p0->link = port;
  144. return port;
  145. }
  146. cmDotRC_t _cmDotNewConnection( cmDot_t* p, const cmChar_t* srcProcStr, const cmChar_t* srcPortStr, const cmChar_t* dstProcStr, const cmChar_t* dstPortStr )
  147. {
  148. cmDotRC_t rc = kOkDotRC;
  149. cmDotProc_t* srcProc;
  150. cmDotProc_t* dstProc;
  151. cmDotPort_t* srcPort;
  152. cmDotPort_t* dstPort;
  153. cmDotConn_t* conn;
  154. cmDotConn_t* c0 = NULL;
  155. cmDotConn_t* c1 = p->conns;
  156. // find the source (output) proc
  157. if((srcProc = _cmDotFindProc(p,srcProcStr)) == NULL )
  158. {
  159. rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The connection source proc instance '%s' could not be found.",cmStringNullGuard(srcProcStr));
  160. goto errLabel;
  161. }
  162. // find the dest (input) proc
  163. if((dstProc = _cmDotFindProc(p,dstProcStr)) == NULL )
  164. {
  165. rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The connection destination proc instance '%s' could not be found.",cmStringNullGuard(dstProcStr));
  166. goto errLabel;
  167. }
  168. // find the source port
  169. if((srcPort = _cmDotNewPort(p,srcProc,srcPortStr)) == NULL )
  170. {
  171. rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The source port %s:%s could not be found or allocated.",cmStringNullGuard(srcProc->instStr),cmStringNullGuard(srcPortStr));
  172. goto errLabel;
  173. }
  174. // find the dest port
  175. if((dstPort = _cmDotNewPort(p,dstProc,dstPortStr)) == NULL )
  176. {
  177. rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The destination port %s:%s could not be found or allocated.",cmStringNullGuard(dstProc->instStr),cmStringNullGuard(dstPortStr));
  178. goto errLabel;
  179. }
  180. conn = cmLhAllocZ(p->lH,cmDotConn_t,1);
  181. conn->srcPort = srcPort;
  182. conn->dstPort = dstPort;
  183. conn->skipFl = _cmDotIsSkipClass(srcProc->classStr) || _cmDotIsSkipClass(dstProc->classStr);
  184. // track the number of connections to each port
  185. if( !conn->skipFl )
  186. {
  187. conn->dstPort->connCnt += 1;
  188. conn->srcPort->connCnt += 1;
  189. }
  190. // set c0 to point to the last connection record
  191. for(; c1!=NULL; c1=c1->link)
  192. c0 = c1;
  193. // make conn the last connection record
  194. if( c0 == NULL )
  195. p->conns = conn;
  196. else
  197. c0->link = conn;
  198. errLabel:
  199. return rc;
  200. }
  201. cmDotRC_t _cmDotNewProc( cmDot_t* p, const cmChar_t* classStr, const cmChar_t* instStr )
  202. {
  203. cmDotRC_t rc = kOkDotRC;
  204. if( instStr==NULL || cmTextLength(instStr)==0 )
  205. return cmErrMsg(&p->err,kInvalidArgDotRC,"A blank or NULL instance label was encountered.");
  206. if( _cmDotFindProc( p, instStr ) )
  207. return cmErrMsg(&p->err,kInvalidArgDotRC,"A duplicate processor instance was encountered ('%s').",instStr);
  208. cmDotProc_t* ndp = cmLhAllocZ(p->lH,cmDotProc_t,1);
  209. ndp->classStr = cmLhAllocStr(p->lH,classStr);
  210. ndp->instStr = cmLhAllocStr(p->lH,instStr);
  211. ndp->outStr = _cmDotSubstitute(p,instStr);
  212. ndp->skipFl = _cmDotIsSkipClass(classStr);
  213. cmDotProc_t* d0p = NULL;
  214. cmDotProc_t* d1p = p->procs;
  215. for(; d1p!=NULL; d1p=d1p->link )
  216. d0p = d1p;
  217. if( d0p == NULL )
  218. p->procs = ndp;
  219. else
  220. d0p->link = ndp;
  221. return rc;
  222. }
  223. unsigned _cmDotProcConnCount( cmDotProc_t* proc )
  224. {
  225. unsigned connN = 0;
  226. cmDotPort_t* port = proc->ports;
  227. for(; port!=NULL; port=port->link)
  228. connN += port->connCnt;
  229. return connN;
  230. }
  231. cmDotRC_t _cmDotWriteOutput( cmDot_t* p, const cmChar_t* outFn )
  232. {
  233. cmDotRC_t rc = kOkDotRC;
  234. cmFileH_t fH = cmFileNullHandle;
  235. cmFileSysPathPart_t* pathParts = cmFsPathParts(outFn);
  236. const cmChar_t* fn = NULL;
  237. if( pathParts == NULL )
  238. {
  239. rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file name '%s' could be parsed.",cmStringNullGuard(outFn));
  240. goto errLabel;
  241. }
  242. if((fn = cmFsMakeFn( pathParts->dirStr, pathParts->fnStr, "dot", NULL )) == NULL )
  243. {
  244. rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file name could not be formed.");
  245. goto errLabel;
  246. }
  247. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  248. {
  249. rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file '%s' could not be created.",cmStringNullGuard(outFn));
  250. goto errLabel;
  251. }
  252. cmFilePrintf(fH,"digraph dsppgm\n{\n node [shape=record]\n");
  253. cmDotProc_t* proc = p->procs;
  254. for(; proc!=NULL; proc=proc->link )
  255. if( proc->skipFl==false && _cmDotProcConnCount(proc)>0 )
  256. {
  257. cmFilePrintf(fH,"\"%s\" [label=\"<n> %s",proc->outStr,proc->outStr);
  258. cmDotPort_t* port = proc->ports;
  259. for(; port!=NULL; port=port->link)
  260. if( port->connCnt > 0 )
  261. cmFilePrintf(fH,"|<p%i> %s",port->portNo,port->labelStr);
  262. cmFilePrintf(fH,"\"];\n");
  263. }
  264. cmDotConn_t* c = p->conns;
  265. for(; c!=NULL; c=c->link)
  266. if( !c->skipFl )
  267. {
  268. cmFilePrintf(fH,"\"%s\":p%i -> \"%s\":p%i;\n",
  269. c->srcPort->proc->outStr,c->srcPort->portNo,
  270. c->dstPort->proc->outStr,c->dstPort->portNo );
  271. }
  272. cmFilePrintf(fH,"}\n");
  273. errLabel:
  274. cmFileClose(&fH);
  275. cmFsFreeFn(fn);
  276. cmFsFreePathParts(pathParts);
  277. return rc;
  278. }
  279. cmDotRC_t cmDspPgmJsonToDot( cmCtx_t* ctx, const cmChar_t* inFn, const cmChar_t* outFn )
  280. {
  281. cmDotRC_t rc = kOkDotRC;
  282. cmJsonH_t jsH = cmJsonNullHandle;
  283. cmJsonNode_t* arr = NULL;
  284. cmJsonNode_t* rp = NULL;
  285. const char* errLbl = NULL;
  286. cmDot_t dot;
  287. unsigned i;
  288. cmDot_t* p = &dot;
  289. memset(p,0,sizeof(dot));
  290. cmErrSetup(&p->err,&ctx->rpt,"cmDspPgmJsonToDot");
  291. // open the pgm description json file
  292. if( cmJsonInitializeFromFile( &jsH, inFn, ctx ) != kOkJsRC )
  293. return cmErrMsg(&p->err,kJsonFailDotRC,"The program description file '%s' could not be opened.",cmStringNullGuard(inFn));
  294. // create an lheap to hold internal data objects
  295. if(cmLHeapIsValid( p->lH = cmLHeapCreate( 8192, ctx))==false )
  296. {
  297. rc = cmErrMsg(&p->err,kLHeapFailDotRC,"The internal LHeap could not be created.");
  298. goto errLabel;
  299. }
  300. // locate the proc instance desc. array in the JSON tree
  301. if((arr = cmJsonFindValue(jsH, "inst_array", NULL, kArrayTId )) == NULL )
  302. {
  303. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'inst_array' tag was not found.");
  304. goto errLabel;
  305. }
  306. // get a count of proc instances
  307. unsigned n = cmJsonChildCount(arr);
  308. // parse each proc instance
  309. for(i=0; i<n; ++i)
  310. {
  311. if((rp = cmJsonArrayElement(arr,i)) == NULL )
  312. {
  313. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'inst_array' element %i was not found.",i);
  314. goto errLabel;
  315. }
  316. cmChar_t* classStr = NULL;
  317. cmChar_t* instStr = NULL;
  318. if( cmJsonMemberValues(rp, &errLbl,
  319. "class", kStringTId, &classStr,
  320. "label", kStringTId, &instStr,
  321. NULL ) != kOkJsRC )
  322. {
  323. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'inst_array' element %i parse failed.",i);
  324. goto errLabel;
  325. }
  326. // create a proc instance data record
  327. _cmDotNewProc( p, classStr, instStr );
  328. }
  329. // locate the connection desc array in the JSON tree
  330. if((arr = cmJsonFindValue(jsH, "conn_array", NULL, kArrayTId)) == NULL )
  331. {
  332. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'conn_array' tag was not found.");
  333. goto errLabel;
  334. }
  335. // get a count of the connections
  336. n = cmJsonChildCount(arr);
  337. // for each connection
  338. for(i=0; i<n; ++i)
  339. {
  340. if((rp = cmJsonArrayElement(arr,i)) == NULL )
  341. {
  342. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'conn_array' element %i was not found.",i);
  343. goto errLabel;
  344. }
  345. cmChar_t* srcStr = NULL;
  346. cmChar_t* srcPortStr = NULL;
  347. cmChar_t* dstStr = NULL;
  348. cmChar_t* dstPortStr = NULL;
  349. if( cmJsonMemberValues(rp, &errLbl,
  350. "sid", kStringTId, &srcStr,
  351. "svar", kStringTId, &srcPortStr,
  352. "did", kStringTId, &dstStr,
  353. "dvar", kStringTId, &dstPortStr,
  354. NULL) != kOkJsRC )
  355. {
  356. rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'conn_array' element %i parse failed.",i);
  357. goto errLabel;
  358. }
  359. // create a connection data record
  360. _cmDotNewConnection( p, srcStr, srcPortStr, dstStr, dstPortStr );
  361. }
  362. rc = _cmDotWriteOutput(p, outFn );
  363. errLabel:
  364. cmJsonFinalize(&jsH);
  365. cmLHeapDestroy(&p->lH);
  366. return rc;
  367. }