一种模块化程序结构
编程本软件遇到了前所未有的困难,出现了乱如麻,理还乱的问题,使用结构化、模块化、面向对象化的程序设计思想,也未能凑效,为此寻找简化问题的途径。经努力寻找到了一种方式,感觉很眼熟,越看越像某种系统,在网上找到医学人体神经网络结构,才豁然开朗,明白了大意,纠正了概念问题,少走了不少探索的弯路。人体是集中信息处理,分散反馈控制的网络,大脑同各中枢交换信息,做出需“思维”的决定。中枢完成各自的内容,比如:丘脑负责感觉,小脑负责运动等。人体以同时同步进行信息处理。大脑的运动意识传递给小脑,小脑解释该让哪儿动哪儿不动,信息通过脊柱传递给脊髓,脊髓通过神经末梢向有关“运动肌肉”发出命令,同时感觉神经末梢通过脊髓调节,神经纤维沿脊柱向上反馈到丘脑,丘脑“分析”后传递给大脑,完成运动感觉。脊髓可自身构成一个小系统,利用传入和传出的神经,构成执行与反馈调节系统,称为“反射弧”,“反射弧”是人体最小的运动反馈执行部分。
模块化:就像盖大楼,需要一块块砖一样,是构建程序的基础。面向对象是仿生遗传解决复杂问题的一种方法,主要解决代码复用问题,将两者结合起来没什么特别的,也是常做的事情,在两者基础上,根据人体神经网络系统,构建面向对象的模块化神经网络结构,以集中信息处理,分散反馈控制的理念解决程序繁乱的问题,是一种行之有效的程序结构方式。
网络结构类型:以某种结构完成信息归并及处理。
1. 单网络型:分布式结构,分为3级,高级模块相当于“大脑”部分,中枢模块相当于“小脑、丘脑”等中枢部分,功能模块相当于“反射弧”部分。单网络型仅含一个高级模块。
2. 多网络型:跨网络组合结构,每个网络的高级模块模仿“大脑”的左右“半脑”同时同步(并联)工作。
模块间结构:各模块不存在任何直接关联,完全独立,通过信息传递,完成相互关联,依赖。如果一个模块调用了另一个模块,则被调用的模块成为子模块,和调用模块一起,视为一个模块。功能模块同功能模块间通信称为局部连接或局部通信,功能模块同中枢模块间通信称为中枢通信或中枢连接,中枢模块同高级模块间通信称为高级通信或高级连接。中枢模块管理功能模块信息,高级模块管理中枢模块信息,构成集中的信息管理系统。
通信链路:模块都有通信部分,简称“通信部”,通信部由输入输出端口构成,输出端口为静态引用,实际存在,输入端口为指针式引用,输入端口指向输出端口,构成一个连接。每个输入或输出端口也称为“节点”,输出端口称“输出节点”,输入端口称“输入节点”,输出节点向后传称为“链路”,向前(回)传称“反射”。
功能模块:实现具体功能,比如读写文件等,以类为基础实现。
1. 功能部:私有,实现相关功能的函数集,耗时长的,在自有线程中执行。还可自调节,实现简单的仿“反射弧”功能。
2. 接口部:公有,由收发通信构成,用于传递信息,引发事件后调用或触发函数,实现相关功能。
2.1 接收:接收中枢模块输出部命令,通过调用或触发功能部函数,实现具体功能。
2.2 发送:通过发送结构,输出功能结果,送达中枢模块输入接口部。
中枢模块:管理协调所属功能模块。
1. 功能部:私有,辅助接口部,完成接口部的工作。
2. 接口部:公有,信息交换,调度,控制。
2.1 高级通信:同高级模块连接,构成系统。
2.1.1 接收:外界信息及资源。
2.1.2 发送:需要外界协助的请求信息。
2.2 中枢通信:通过端口,协调各功能模块。
2.2.1 接收:接收功能模块信息,协调功能模块完成组合功能。
2.2.2 发送:向有关功能模块发出操作命令,触发实现其功能。
高级模块:管理协调所有中枢模块。
(目前未用到,待验证)
高级模块和中枢模块:
通过一个小的局部项目,简单验证这一套想法,是有意义的,目前正在做一个可以独立方便编辑的压缩视频文件,名字为 Fic (Frame Intra Compression File) 帧内压缩文件,先从AVI文件提取RGB视频,暂不压缩,保存为Fic文件,然后再播放,模块结构图如下:
链路流程如下:
目前已初步实现,中枢模块仅实现了交换机功能,部分定义如下:
//模块列表 #define OpModule 1 //操作模块端口编号 #define FicModule 2 //Fic模块端口编号 #define AviModule 3 //Avi模块端口编号 #define MapModule 4 //Map模块端口编号 #define SodModule 5 //Sod模块端口编号 //FIC模块命令集 #define CMD_FIC_OpenR 1 //打开准备读文件 #define CMD_FIC_OpenW 2 //打开准备写文件 #define CMD_FIC_DeFrame 3 //FIC压缩一帧 #define CMD_FIC_EnFrame 4 //FIC压缩一帧 //AVI模块命令集 #define CMD_AVI_OpenR 5 //打开准备读文件 #define CMD_AVI_OpenW 6 //打开准备写文件 #define CMD_AVI_DeFrame 7 //AVI解压一帧 #define CMD_AVI_EnFrame 8 //AVI压缩一帧 //Map模块命令集 #define CMD_Map_SetParam 9//设置屏幕参数 #define CMD_Map_Updata 10 //更新屏幕
主窗体实现模块连接:
//本地命令连接 AviCentral.pRxPort1 = &TxPort1;//Tx1连接,操作模块(命令)->中枢模块 pRxPort1 = &AviCentral.TxPort1;//Rx1连接,操作模块(监视)<-中枢模块 //Fic模块连接 AviCentral.pRxPort2 = &FicFile.TxPort1;//Tx2连接,Fic模块(发送)->中枢模块 FicFile.pRxPort1 = &AviCentral.TxPort2;//Rx2连接,Fic模块(命令)<-中枢模块 //Avi模块连接 AviCentral.pRxPort3 = &AviFile.TxPort1;//Tx3连接,Avi模块(发送)->中枢模块 AviFile.pRxPort1 = &AviCentral.TxPort3;//Rx3连接,Avi模块(命令)<-中枢模块 //Map模块连接 AviCentral.pRxPort4 = &MapScreen.TxPort1;//Tx4连接,Map模块(发送)->中枢模块 MapScreen.pRxPort1 = &AviCentral.TxPort4;//Rx4连接,Map模块(命令)<-中枢模块
临时通过定时器验证Fic文件,链路流程如下:
已看到画面,RGB位序可能反了,颜色不正常,打开文件播放时再打开文件出错没有考虑,待进一步排除后,继续进行。
颜色不正常问题已解决,因为使用vfw调用,输出一帧数据去掉头长度后,怎么调都不是正常颜色,难道是色差信号?换用其他方式提取视频,以节省时间。
以前用过OpenCv提取视频,需要安装,调用静态库,BCB静态库和VC格式不同,用的是以前转化过的,基础工作完以后,顺利得到RGB图像视频,通过RGB视频帧播放,颜色正常。继续解决问题。
需要释放打开的资源后,才能从新打开,“打开”与“释放”必须成对,否则崩溃。通过bIsOpen标志标识打开过没有,打开前先“释放”一下,同时避免正在播放时,退出播放器,“资源”可能被操作系统清除不干净,那可惨了,所以,在模块类销毁前,“释放”一下,避免悲剧发生。问题集中在“释放”也就是停止播放的功能上,不管是自动停止还是手工按钮停止,都要有“停止”的功能,目前中枢模块仅实现了交换机功能,也就是只能执行串联链路功能,从一个端口进,到任意一个端口出,任意一个端口都可以这样。“停止”方式并联比较好,可以同时到达需要“停止”的模块,串化链路目前是固定的,只能沿一个方向走,要改成“万能”的,事先规划好“路线”。当前操作模块的这一部分功能:
DWORD WINAPI TForm1::OperationThread(LPVOID lpParam)//操作线程 { TForm1 *pThread = (TForm1 *) lpParam; DWORD dwResult; float fTmp0,fTmp1; DWORD dwFrameInterval,dwFrameRate; while(1) { if(pThread->pRxPort1->bActive)//接收端口1 { switch(pThread->pRxPort1->dwCommand)//命令分派 { case CMD_FIC_OpenR: if(pThread->pRxPort1->bOK)//打开成功 { pThread->ST4->Caption = pThread->pRxPort1->dwParam[0];//帧数量 pThread->ST1->Caption = pThread->pRxPort1->dwParam[1];//AVI宽度 pThread->ST2->Caption = pThread->pRxPort1->dwParam[2];//AVI高度 //帧率计算 if(pThread->pRxPort1->dwParam[4] != 0) { fTmp0 = (float)pThread->pRxPort1->dwParam[3] / (float)pThread->pRxPort1->dwParam[4]; } dwFrameRate = (DWORD)fTmp0; pThread->ST5->Caption = dwFrameRate;//显示帧率 //计算帧间延时 if(fTmp0 != 0.0) fTmp1 = 1000.0 / fTmp0; dwFrameInterval = (DWORD)fTmp1; pThread->ST6->Caption = IntToStr(dwFrameInterval) + "ms";//帧间延时 pThread->ST7->Caption = pThread->pRxPort1->dwParam[3];//比率 pThread->ST8->Caption = pThread->pRxPort1->dwParam[4];//刻度 pThread->TxPort1.bActive = true;//启动命令 pThread->TxPort1.dwToModule = MapModule;//到Map模块 pThread->TxPort1.dwCommand = CMD_Map_SetParam;//设置屏幕参数 pThread->TxPort1.dwParam[0] = pThread->pRxPort1->dwParam[1];//AVI宽度 pThread->TxPort1.dwParam[1] = pThread->pRxPort1->dwParam[2];//AVI高度 } else { MessageBox(HWND_DESKTOP, "打开Fic文件读失败", "错误", MB_OK | MB_ICONINFORMATION); } break; case CMD_FIC_OpenW: if(pThread->pRxPort1->bOK)//打开写成功 { if(pThread->pRxPort1->dwParam[0] > 0)//大于一帧判断 { pThread->TxPort1.bActive = true;//启动命令 pThread->TxPort1.dwCommand = CMD_AVI_DeFrame;//Avi解压命令 pThread->TxPort1.dwToModule = AviModule;//链式,前去解压 } else { MessageBox(HWND_DESKTOP, "无视频帧数据。", "失败", MB_OK | MB_ICONINFORMATION); } } else { MessageBox(HWND_DESKTOP, "打开Fic文件写失败", "错误", MB_OK | MB_ICONINFORMATION); } break; case CMD_FIC_DeFrame: if(pThread->pRxPort1->bOK)//已解压 { pThread->ProgressBar1->Position = pThread->pRxPort1->dwParam[0];//显示进度 pThread->ST10->Caption = pThread->pRxPort1->dwParam[0];//显示帧号 pThread->TxPort1.bActive = true;//启动命令 pThread->TxPort1.dwToModule = MapModule;//到Map模块 pThread->TxPort1.dwCommand = CMD_Map_Updata;//显示视频数据命令 pThread->TxPort1.pPtr = pThread->pRxPort1->pPtr;//传递指针 } else { pThread->ProgressBar1->Position = 0;//显示进度 pThread->ST100->Caption = " 播放Fic文件已完成。"; } break;//显示播放帧属性 //----------------------------------------------------------------------------------------------------- case CMD_AVI_OpenR: if(pThread->pRxPort1->bOK)//打开读Avi文件成功 { pThread->ST4->Caption = pThread->pRxPort1->dwParam[0];//帧数量 pThread->ST1->Caption = pThread->pRxPort1->dwParam[1];//AVI宽度 pThread->ST2->Caption = pThread->pRxPort1->dwParam[2];//AVI高度 pThread->ST5->Caption = FormatFloat("0.000", pThread->pRxPort1->fParam[0]);//显示帧率 pThread->TxPort1.bActive = true;//启动命令 pThread->TxPort1.dwToModule = MapModule;//到Map模块 pThread->TxPort1.dwCommand = CMD_Map_SetParam;//设置屏幕参数 pThread->TxPort1.dwParam[0] = pThread->pRxPort1->dwParam[1];//AVI宽度 pThread->TxPort1.dwParam[1] = pThread->pRxPort1->dwParam[2];//AVI高度 pThread->TxPort1.dwParam[2] = pThread->pRxPort1->dwParam[3];//帧字节长度 } else { MessageBox(HWND_DESKTOP, "打开Avi文件失败", "错误", MB_OK | MB_ICONINFORMATION); } break; case CMD_AVI_DeFrame: pThread->ProgressBar1->Position = pThread->pRxPort1->dwParam[0];//显示进度 pThread->ST10->Caption = pThread->pRxPort1->dwParam[0];//显示帧号 if(pThread->pRxPort1->bOK)//已解压 { //pThread->TxPort1.bActive = true;//启动命令 //pThread->TxPort1.dwCommand = CMD_FIC_EnFrame;//Fic压缩命令 //pThread->TxPort1.dwToModule = FicModule;//链式,到Fic模块 //pThread->TxPort1.pPtr = pThread->pRxPort1->pPtr;//传递指针 pThread->TxPort1.bActive = true;//启动命令 pThread->TxPort1.dwToModule = MapModule;//到Map模块 pThread->TxPort1.dwCommand = CMD_Map_Updata;//显示视频数据命令 pThread->TxPort1.pPtr = pThread->pRxPort1->pPtr;//传递指针 } else { pThread->Timer1->Enabled = false;//临时 关闭定时器 pThread->ProgressBar1->Position = 0;//显示进度 pThread->ST100->Caption = " 写Fic文件已完成。"; } break; } pThread->pRxPort1->bActive = false;//关闭本次命令处理 } dwResult = WaitForSingleObject(pThread->hEventExit, 1); if(dwResult == WAIT_OBJECT_0) break;//退出判断 } return 0; }
通信端口结构:
struct TCommPort//通信端口结构 { bool bActive,//命令有效标志 bOK;//成功或失败 DWORD dwToModule,//目的模块 dwCommand,//命令 dwParam[8];//参数 float fParam[2];//浮点参数 BYTE *pPtr;//指针参数 String sStr;//字符串 };
当前中枢模块相关部分:
void __fastcall TAviCentral::Port1To(TCommPort *pInPort)//端口1到 { switch(pInPort->dwToModule) { case OptModule: memcpy(&TxPort1, pRxPort1, sizeof(TCommPort));//传递到端口1发送 break; case FicModule: memcpy(&TxPort2, pRxPort1, sizeof(TCommPort));//传递到端口2发送 break; case AviModule: memcpy(&TxPort3, pRxPort1, sizeof(TCommPort));//传递到端口3发送 break; case MapModule: memcpy(&TxPort4, pRxPort1, sizeof(TCommPort));//传递到端口4发送 break; } } void __fastcall TAviCentral::Port2To(TCommPort *pInPort)//端口2到 { switch(pInPort->dwToModule) { case OptModule: memcpy(&TxPort1, pRxPort2, sizeof(TCommPort));//传递到端口1发送 break; case FicModule: memcpy(&TxPort2, pRxPort2, sizeof(TCommPort));//传递到端口2发送 break; case AviModule: memcpy(&TxPort3, pRxPort2, sizeof(TCommPort));//传递到端口3发送 break; case MapModule: memcpy(&TxPort4, pRxPort2, sizeof(TCommPort));//传递到端口4发送 break; } } void __fastcall TAviCentral::Port3To(TCommPort *pInPort)//端口3到 { switch(pInPort->dwToModule) { case OptModule: memcpy(&TxPort1, pRxPort3, sizeof(TCommPort));//传递到端口1发送 break; case FicModule: memcpy(&TxPort2, pRxPort3, sizeof(TCommPort));//传递到端口2发送 break; case AviModule: memcpy(&TxPort3, pRxPort3, sizeof(TCommPort));//传递到端口3发送 break; case MapModule: memcpy(&TxPort4, pRxPort3, sizeof(TCommPort));//传递到端口4发送 break; } } void __fastcall TAviCentral::Port4To(TCommPort *pInPort)//端口4到 { switch(pInPort->dwToModule) { case OptModule: memcpy(&TxPort1, pRxPort4, sizeof(TCommPort));//传递到端口1发送 break; case FicModule: memcpy(&TxPort2, pRxPort4, sizeof(TCommPort));//传递到端口2发送 break; case AviModule: memcpy(&TxPort3, pRxPort4, sizeof(TCommPort));//传递到端口3发送 break; case MapModule: memcpy(&TxPort4, pRxPort4, sizeof(TCommPort));//传递到端口4发送 break; } } void __fastcall TAviCentral::ToPort(TCommPort *pInPort, DWORD dwPortNum)//到端口 { switch(dwPortNum) { case OptModule: Port1To(pInPort);//端口1到 break; case FicModule: Port2To(pInPort);//端口2到 break; case AviModule: Port3To(pInPort);//端口3到 break; case MapModule: Port4To(pInPort);//端口4到 break; } } DWORD WINAPI TAviCentral::CThread(LPVOID lpParam)//工作线程 { TAviCentral *pThread = (TAviCentral *) lpParam; DWORD dwResult; while(1) { if(pThread->pRxPort1->bActive)//监视输入端口1,转接输出到其他端口 { pThread->ToPort(pThread->pRxPort1, OptModule);//到端口 pThread->pRxPort1->bActive = false;//关闭本次命令处理 } if(pThread->pRxPort2->bActive)//监视输入端口2,Fic模块反馈 { pThread->ToPort(pThread->pRxPort2, FicModule);//到端口 pThread->pRxPort2->bActive = false;//关闭本次命令处理 } if(pThread->pRxPort3->bActive)//监视输入端口3,Avi模块反馈 { pThread->ToPort(pThread->pRxPort3, AviModule);//到端口 pThread->pRxPort3->bActive = false;//关闭本次命令处理 } if(pThread->pRxPort4->bActive)//监视输入端口4,Map模块反馈 { pThread->ToPort(pThread->pRxPort4, MapModule);//到端口 pThread->pRxPort4->bActive = false;//关闭本次命令处理 } dwResult = WaitForSingleObject(pThread->hEventExit, 1); if(dwResult == WAIT_OBJECT_0) break;//退出判断 } return 0; }
再次从病理医学网了解到:中枢神经系统像是一部容器巨大的信息加工器,加工的结果可以出现反射活动、产生感觉或记忆。继续改进中枢模块。
经过几天努力,未能实现预置指令链路,还是只能实时单步步进,改进了一点,可以并联执行指令,提高了指令效率,目前进度到临时通过定时器触发播放Avi文件,界面如下:
通过复制通信结构,实现多指令并联,改进的通信结构代码如下:
struct TSCommPort//单通信端口结构 { bool bActive,//命令有效标志 bOK;//成功或失败 DWORD dwToModule,//目的模块 dwCommand,//命令 dwParam[8],//参数 dwOptModel;//操作模式 float fParam[2];//浮点参数 BYTE *pPtr;//指针参数 String sStr;//字符串 }; struct TMCommPort//多通信端口结构 { bool bActive;//命令有效标志 DWORD dwCmdCount;//命令数量 TSCommPort Param[def_MaxCmdCount]; };
通信结构增加dwOptModel操作模式,实现分支控制。改进的播放Avi文件指令链流程图如下:
播放正常。
已实现Fic文件的播放,对流程链路进行了改进和整理,如下图:
对这一架构的探索和设想暂告一段落,初步实现了部分想法,总结如下:
优点:简化理顺函数、模块等相互之间的关系,虽然也很“复杂”,但增强了条理性,尽可能避免“乱麻”等头绪繁乱问题。对于复杂程序,是需要流程图的,链路图类似于流程图,且直接使用,使得问题实际得到简化,流程图虽然“复杂”,但可认为是最简单的。
缺点:正是因为切断了函数、模块等之间的直接调用、依赖,中间加入了通信环节,增加了处理延时,而且是多倍的,因为各模块使用线程“独立自主”处理问题,使得延时问题加重。
展望:通过对这一构想的理解、“进化”,希望建立一套理论、概念,实际应用,例程等,使问题“清晰”,更加“有据可依”。希望引起注意,如果真的有优点,那就有推广意义。
本文结束。