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

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