编程本软件遇到了前所未有的困难,出现了乱如麻,理还乱的问题,使用结构化、模块化、面向对象化的程序设计思想,也未能凑效,为此寻找简化问题的途径。经努力寻找到了一种方式,感觉很眼熟,越看越像某种系统,在网上找到医学人体神经网络结构,才豁然开朗,明白了大意,纠正了概念问题,少走了不少探索的弯路。人体是集中信息处理,分散反馈控制的网络,大脑同各中枢交换信息,做出需“思维”的决定。中枢完成各自的内容,比如:丘脑负责感觉,小脑负责运动等。人体以同时同步进行信息处理。大脑的运动意识传递给小脑,小脑解释该让哪儿动哪儿不动,信息通过脊柱传递给脊髓,脊髓通过神经末梢向有关“运动肌肉”发出命令,同时感觉神经末梢通过脊髓调节,神经纤维沿脊柱向上反馈到丘脑,丘脑“分析”后传递给大脑,完成运动感觉。脊髓可自身构成一个小系统,利用传入和传出的神经,构成执行与反馈调节系统,称为“反射弧”,“反射弧”是人体最小的运动反馈执行部分。
模块化:就像盖大楼,需要一块块砖一样,是构建程序的基础。面向对象是仿生遗传解决复杂问题的一种方法,主要解决代码复用问题,将两者结合起来没什么特别的,也是常做的事情,在两者基础上,根据人体神经网络系统,构建面向对象的模块化神经网络结构,以集中信息处理,分散反馈控制的理念解决程序繁乱的问题,是一种行之有效的程序结构方式。
网络结构类型:以某种结构完成信息归并及处理。
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文件的播放,对流程链路进行了改进和整理,如下图:
对这一架构的探索和设想暂告一段落,初步实现了部分想法,总结如下:
优点:简化理顺函数、模块等相互之间的关系,虽然也很“复杂”,但增强了条理性,尽可能避免“乱麻”等头绪繁乱问题。对于复杂程序,是需要流程图的,链路图类似于流程图,且直接使用,使得问题实际得到简化,流程图虽然“复杂”,但可认为是最简单的。
缺点:正是因为切断了函数、模块等之间的直接调用、依赖,中间加入了通信环节,增加了处理延时,而且是多倍的,因为各模块使用线程“独立自主”处理问题,使得延时问题加重。
展望:通过对这一构想的理解、“进化”,希望建立一套理论、概念,实际应用,例程等,使问题“清晰”,更加“有据可依”。希望引起注意,如果真的有优点,那就有推广意义。
本文结束。