libcm is a C development framework with an emphasis on audio signal processing applications.
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.

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. }