From 6c792e240dfc74325b1cf918c7aa92407d605a00 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 25 Sep 2015 18:48:44 -0400 Subject: [PATCH] cmDspPgmJsonToDot.h/c, Makefile : Initial working version of cmDspPgmJsonToDot --- Makefile.am | 4 +- app/cmDspPgmJsonToDot.c | 399 +++++++++++++++++++++++++++++++++++++--- app/cmDspPgmJsonToDot.h | 10 +- 3 files changed, 383 insertions(+), 30 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3fc4ff1..76bc545 100644 --- a/Makefile.am +++ b/Makefile.am @@ -77,8 +77,8 @@ cmSRC += src/libcm/cmProcObj.c src/libcm/cmProc.c src/libcm/cmProc2.c src/libcm/ cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmScoreProc.h cmSRC += src/libcm/app/cmOnset.c src/libcm/app/cmTimeLine.c src/libcm/app/cmScore.c src/libcm/app/cmScoreProc.c -cmHDR += src/libcm/app/cmSdb.h src/libcm/app/cmTakeSeqBldr.h -cmSRC += src/libcm/app/cmSdb.c src/libcm/app/cmTakeSeqBldr.c +cmHDR += src/libcm/app/cmSdb.h src/libcm/app/cmTakeSeqBldr.h src/libcm/app/cmDspPgmJsonToDot.h +cmSRC += src/libcm/app/cmSdb.c src/libcm/app/cmTakeSeqBldr.c src/libcm/app/cmDspPgmJsonToDot.c cmHDR += src/libcm/app/cmPickup.h src/libcm/cmRbm.h src/libcm/cmTaskMgr.h src/libcm/cmSyncRecd.h cmSRC += src/libcm/app/cmPickup.c src/libcm/cmRbm.c src/libcm/cmTaskMgr.c src/libcm/cmSyncRecd.c diff --git a/app/cmDspPgmJsonToDot.c b/app/cmDspPgmJsonToDot.c index 1d4d3e1..d6c04e2 100644 --- a/app/cmDspPgmJsonToDot.c +++ b/app/cmDspPgmJsonToDot.c @@ -1,85 +1,430 @@ +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmFloatTypes.h" +#include "cmComplexTypes.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmLinkedHeap.h" +#include "cmSymTbl.h" +#include "cmJson.h" +#include "cmText.h" +#include "cmDspPgmJsonToDot.h" +#include "cmFile.h" +#include "cmFileSys.h" + +struct cmDotProc_str; typedef struct cmDotPort_str { - cmChar_t* label; + struct cmDotProc_str* proc; + cmChar_t* labelStr; + unsigned portNo; + unsigned connCnt; // count of connections to this port struct cmDotPort_str* link; } cmDotPort_t; typedef struct cmDotProc_str { - cmChar_t* class; - cmChar_t* inst; + cmChar_t* classStr; + cmChar_t* instStr; + cmChar_t* outStr; + unsigned portCnt; + bool skipFl; - cmDotPort_t* list; + cmDotPort_t* ports; struct cmDotProc_str* link; } cmDotProc_t; +typedef struct cmDotConn_str +{ + cmDotPort_t* srcPort; // output + cmDotPort_t* dstPort; // input + bool skipFl; + struct cmDotConn_str* link; +} cmDotConn_t; + + typedef struct cmDot_str { - cmDotProc_t* list; + cmErr_t err; + cmLHeapH_t lH; + cmDotProc_t* procs; + cmDotConn_t* conns; } cmDot_t; - -void _cmDotNewProc( cmDot_t* p, const cmChar_t* class, const cmChar_t* inst ) +typedef struct { + const cmChar_t* s0; + const cmChar_t* s1; +} cmDotSubst_t; + +cmDotSubst_t _cmDotSubstArray[] = +{ + { "Router", "Rtr" }, + { "Scalar", "Sc" }, + { "ScaleRange", "SR" }, + { "MsgList", "ML" }, + { "Button", "Btn" }, + { "PortToSym", "PtS" }, + { "1ofN", "lOfN"}, + { NULL, NULL } +}; + +const cmChar_t* _cmDotSkipClassArray[] = +{ + "Scalar", + NULL +}; + +void _cmDotReplaceDash( cmChar_t* str ) +{ + cmChar_t* s = str; + for(; *s; ++s ) + if( *s == '-' ) + *s = '_'; +} + +cmChar_t* _cmDotSubstitute( cmDot_t* p, const cmChar_t* label ) +{ + unsigned i; + cmChar_t* s = cmLhAllocStr(p->lH,label); + for(i=0; _cmDotSubstArray[i].s0 != NULL; ++i) + { + unsigned n0 = cmTextLength(_cmDotSubstArray[i].s0); + + if( cmTextCmpN( _cmDotSubstArray[i].s0, s, n0 ) == 0 ) + { + unsigned n1 = cmTextLength(_cmDotSubstArray[i].s1); + assert(n0>=n1); + cmTextShrinkS(s,s+n1,n0-n1); + strncpy(s,_cmDotSubstArray[i].s1,n1); + } + } + + return s; } -void _cmDotNewPort( cmDot_t* p, const cmChar_t* srcStr, const cmChar_t* srcPortStr, const cmChar_t* dstStr, const cmChar_t* dstPortStr ) +bool _cmDotIsSkipClass( const cmChar_t* classStr ) { + unsigned i; + + for(i=0; _cmDotSkipClassArray[i]!=NULL; ++i) + if( cmTextCmp(_cmDotSkipClassArray[i],classStr) == 0 ) + return true; + + return false; } +cmDotPort_t* _cmDotFindPort( cmDotProc_t* proc, const cmChar_t* labelStr ) +{ + cmDotPort_t* port = proc->ports; + for(; port!=NULL; port=port->link) + if( cmTextCmp(port->labelStr,labelStr) == 0 ) + return port; + + return NULL; +} + +cmDotProc_t* _cmDotFindProc( cmDot_t* p, const cmChar_t* instStr ) +{ + cmDotProc_t* dp = p->procs; + for(; dp!=NULL; dp=dp->link) + if( cmTextCmp(dp->instStr,instStr) == 0 ) + return dp; + + return NULL; +} + +cmDotPort_t* _cmDotNewPort( cmDot_t* p, cmDotProc_t* proc, const cmChar_t* labelStr ) +{ + cmDotPort_t* port = NULL; + + if( labelStr==NULL || cmTextLength(labelStr)==0 ) + { + cmErrMsg(&p->err,kInvalidArgDotRC,"A blank port label was encountered."); + return NULL; + } + + if((port = _cmDotFindPort(proc,labelStr)) != NULL ) + return port; + + port = cmLhAllocZ(p->lH,cmDotPort_t,1); + + port->proc = proc; + port->labelStr = cmLhAllocStr(p->lH,labelStr); + port->portNo = proc->portCnt; + + proc->portCnt += 1; + + cmDotPort_t* p0 = NULL; + cmDotPort_t* p1 = proc->ports; + for(; p1!=NULL; p1=p1->link) + p0 = p1; + + if( p0 == NULL ) + proc->ports = port; + else + p0->link = port; + + return port; +} + +cmDotRC_t _cmDotNewConnection( cmDot_t* p, const cmChar_t* srcProcStr, const cmChar_t* srcPortStr, const cmChar_t* dstProcStr, const cmChar_t* dstPortStr ) +{ + cmDotRC_t rc = kOkDotRC; + cmDotProc_t* srcProc; + cmDotProc_t* dstProc; + cmDotPort_t* srcPort; + cmDotPort_t* dstPort; + cmDotConn_t* conn; + cmDotConn_t* c0 = NULL; + cmDotConn_t* c1 = p->conns; + + // find the source (output) proc + if((srcProc = _cmDotFindProc(p,srcProcStr)) == NULL ) + { + rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The connection source proc instance '%s' could not be found.",cmStringNullGuard(srcProcStr)); + goto errLabel; + } + + + // find the dest (input) proc + if((dstProc = _cmDotFindProc(p,dstProcStr)) == NULL ) + { + rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The connection destination proc instance '%s' could not be found.",cmStringNullGuard(dstProcStr)); + goto errLabel; + } + + // find the source port + if((srcPort = _cmDotNewPort(p,srcProc,srcPortStr)) == NULL ) + { + rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The source port %s:%s could not be found or allocated.",cmStringNullGuard(srcProc->instStr),cmStringNullGuard(srcPortStr)); + goto errLabel; + } + + // find the dest port + if((dstPort = _cmDotNewPort(p,dstProc,dstPortStr)) == NULL ) + { + rc = cmErrMsg(&p->err,kInvalidArgDotRC,"The destination port %s:%s could not be found or allocated.",cmStringNullGuard(dstProc->instStr),cmStringNullGuard(dstPortStr)); + goto errLabel; + } + + conn = cmLhAllocZ(p->lH,cmDotConn_t,1); + + conn->srcPort = srcPort; + conn->dstPort = dstPort; + conn->skipFl = _cmDotIsSkipClass(srcProc->classStr) || _cmDotIsSkipClass(dstProc->classStr); + + // track the number of connections to each port + if( !conn->skipFl ) + { + conn->dstPort->connCnt += 1; + conn->srcPort->connCnt += 1; + } + + + // set c0 to point to the last connection record + for(; c1!=NULL; c1=c1->link) + c0 = c1; + + // make conn the last connection record + if( c0 == NULL ) + p->conns = conn; + else + c0->link = conn; + + errLabel: + return rc; +} + + +cmDotRC_t _cmDotNewProc( cmDot_t* p, const cmChar_t* classStr, const cmChar_t* instStr ) +{ + cmDotRC_t rc = kOkDotRC; + + if( instStr==NULL || cmTextLength(instStr)==0 ) + return cmErrMsg(&p->err,kInvalidArgDotRC,"A blank or NULL instance label was encountered."); + + if( _cmDotFindProc( p, instStr ) ) + return cmErrMsg(&p->err,kInvalidArgDotRC,"A duplicate processor instance was encountered ('%s').",instStr); + + cmDotProc_t* ndp = cmLhAllocZ(p->lH,cmDotProc_t,1); + + ndp->classStr = cmLhAllocStr(p->lH,classStr); + ndp->instStr = cmLhAllocStr(p->lH,instStr); + ndp->outStr = _cmDotSubstitute(p,instStr); + ndp->skipFl = _cmDotIsSkipClass(classStr); + + cmDotProc_t* d0p = NULL; + cmDotProc_t* d1p = p->procs; + + for(; d1p!=NULL; d1p=d1p->link ) + d0p = d1p; + + if( d0p == NULL ) + p->procs = ndp; + else + d0p->link = ndp; + + return rc; +} + +unsigned _cmDotProcConnCount( cmDotProc_t* proc ) +{ + unsigned connN = 0; + + cmDotPort_t* port = proc->ports; + for(; port!=NULL; port=port->link) + connN += port->connCnt; + + return connN; +} + +cmDotRC_t _cmDotWriteOutput( cmDot_t* p, const cmChar_t* outFn ) +{ + cmDotRC_t rc = kOkDotRC; + + cmFileH_t fH = cmFileNullHandle; + + cmFileSysPathPart_t* pathParts = cmFsPathParts(outFn); + const cmChar_t* fn = NULL; + + if( pathParts == NULL ) + { + rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file name '%s' could be parsed.",cmStringNullGuard(outFn)); + goto errLabel; + } + + if((fn = cmFsMakeFn( pathParts->dirStr, pathParts->fnStr, "dot", NULL )) == NULL ) + { + rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file name could not be formed."); + goto errLabel; + } + + if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC ) + { + rc = cmErrMsg(&p->err,kFileFailDotRC,"The output file '%s' could not be created.",cmStringNullGuard(outFn)); + goto errLabel; + } + + cmFilePrintf(fH,"digraph dsppgm\n{\n node [shape=record]\n"); + + cmDotProc_t* proc = p->procs; + for(; proc!=NULL; proc=proc->link ) + if( proc->skipFl==false && _cmDotProcConnCount(proc)>0 ) + { + cmFilePrintf(fH,"\"%s\" [label=\" %s",proc->outStr,proc->outStr); + + cmDotPort_t* port = proc->ports; + for(; port!=NULL; port=port->link) + if( port->connCnt > 0 ) + cmFilePrintf(fH,"| %s",port->portNo,port->labelStr); + + cmFilePrintf(fH,"\"];\n"); + } + + cmDotConn_t* c = p->conns; + for(; c!=NULL; c=c->link) + if( !c->skipFl ) + { + cmFilePrintf(fH,"\"%s\":p%i -> \"%s\":p%i;\n", + c->srcPort->proc->outStr,c->srcPort->portNo, + c->dstPort->proc->outStr,c->dstPort->portNo ); + } + + cmFilePrintf(fH,"}\n"); + + errLabel: + cmFileClose(&fH); + cmFsFreeFn(fn); + cmFsFreePathParts(pathParts); + return rc; +} cmDotRC_t cmDspPgmJsonToDot( cmCtx_t* ctx, const cmChar_t* inFn, const cmChar_t* outFn ) { - cmDotRC_t rc = kOkDotRC; - cmJsonH_t jsH = cmJsonNullHandle; - cmErr_t err; - cmJsonNode_t* arr; - cmJsonNode_t* rp; - const char* errLbl = NULL; - - cmErrSetup(&err,&ctx->rpt,"cmDspPgmJsonToDot"); - - if( cmJsonInitializeFromFile( &jsH, inFn, ctx ) != kOkJsRC ) - return cmErrMsg(&err,kJsonFailDotRC,"The program description file '%s' could not be opened.",cmStringNullGuard(inFn)); + cmDotRC_t rc = kOkDotRC; + cmJsonH_t jsH = cmJsonNullHandle; + cmJsonNode_t* arr = NULL; + cmJsonNode_t* rp = NULL; + const char* errLbl = NULL; + cmDot_t dot; + unsigned i; + cmDot_t* p = ˙ - if((arr = cmJsonFindValue(jsH, "inst_array", NULL, kArrayTId )) == NULL ) + memset(p,0,sizeof(dot)); + + cmErrSetup(&p->err,&ctx->rpt,"cmDspPgmJsonToDot"); + + // open the pgm description json file + if( cmJsonInitializeFromFile( &jsH, inFn, ctx ) != kOkJsRC ) + return cmErrMsg(&p->err,kJsonFailDotRC,"The program description file '%s' could not be opened.",cmStringNullGuard(inFn)); + + // create an lheap to hold internal data objects + if(cmLHeapIsValid( p->lH = cmLHeapCreate( 8192, ctx))==false ) { + rc = cmErrMsg(&p->err,kLHeapFailDotRC,"The internal LHeap could not be created."); + goto errLabel; } + // locate the proc instance desc. array in the JSON tree + if((arr = cmJsonFindValue(jsH, "inst_array", NULL, kArrayTId )) == NULL ) + { + rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'inst_array' tag was not found."); + goto errLabel; + } + + // get a count of proc instances unsigned n = cmJsonChildCount(arr); - + + // parse each proc instance for(i=0; ierr,kJsonSyntaxErrDotRC,"The 'inst_array' element %i was not found.",i); + goto errLabel; } cmChar_t* classStr = NULL; cmChar_t* instStr = NULL; if( cmJsonMemberValues(rp, &errLbl, "class", kStringTId, &classStr, - "label", kStringTId, &instStr + "label", kStringTId, &instStr, NULL ) != kOkJsRC ) { + rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'inst_array' element %i parse failed.",i); + goto errLabel; } + // create a proc instance data record _cmDotNewProc( p, classStr, instStr ); } + // locate the connection desc array in the JSON tree if((arr = cmJsonFindValue(jsH, "conn_array", NULL, kArrayTId)) == NULL ) { + rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'conn_array' tag was not found."); + goto errLabel; } - unsigned n = cmJsonChildCount(arr); + // get a count of the connections + n = cmJsonChildCount(arr); + // for each connection for(i=0; ierr,kJsonSyntaxErrDotRC,"The 'conn_array' element %i was not found.",i); + goto errLabel; } cmChar_t* srcStr = NULL; @@ -94,17 +439,21 @@ cmDotRC_t cmDspPgmJsonToDot( cmCtx_t* ctx, const cmChar_t* inFn, const cmChar_t* "dvar", kStringTId, &dstPortStr, NULL) != kOkJsRC ) { + rc = cmErrMsg(&p->err,kJsonSyntaxErrDotRC,"The 'conn_array' element %i parse failed.",i); + goto errLabel; } - _cmDotNewPort( p, srcStr, srcPortStr, dstStr, dstPortStr ); - + // create a connection data record + _cmDotNewConnection( p, srcStr, srcPortStr, dstStr, dstPortStr ); } + rc = _cmDotWriteOutput(p, outFn ); errLabel: cmJsonFinalize(&jsH); + cmLHeapDestroy(&p->lH); return rc; } diff --git a/app/cmDspPgmJsonToDot.h b/app/cmDspPgmJsonToDot.h index f72ae91..6991161 100644 --- a/app/cmDspPgmJsonToDot.h +++ b/app/cmDspPgmJsonToDot.h @@ -7,13 +7,17 @@ extern "C" { enum { - kOkDotRC, - kJsonFailDotRC + kOkDotRC = cmOkRC, + kJsonFailDotRC, + kJsonSyntaxErrDotRC, + kInvalidArgDotRC, + kLHeapFailDotRC, + kFileFailDotRC }; typedef unsigned cmDotRC_t; - + cmDotRC_t cmDspPgmJsonToDot( cmCtx_t* ctx, const cmChar_t* inFn, const cmChar_t* outFn ); #ifdef __cplusplus }