⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 myclass.pas

📁 这是一个火车站公寓的自动叫班程序源码
💻 PAS
📖 第 1 页 / 共 2 页
字号:
unit MyClass;

interface

uses  Common,Classes,MPlayer,SysUtils,DBTables,ExtCtrls,Buttons,
      ComCtrls,Dialogs, ADODB, DB, Windows, DBGrids;

Type
   TWavHeader = record //定义一个Wav文件头格式
    rId : longint;
    rLen : longint;
    wId : longint;
    fId : longint;
    fLen : longint;
    wFormatTag : word;
    nChannels : word;
    nSamplesPerSec : longint;
    nAvgBytesPerSec : longint;
    nBlockAlign : word;
    wBitsPerSample : word;
    dId : longint;
    wSampleLength : longint;
  end;

   TAutoCaller=class(TThread)
   private
     fRoomDetectStarted:boolean;
     FLineBusy:boolean;
     FKeyAcknowledged:boolean;
     procedure SetRoomDetectStart(value:boolean);
     procedure CreateWav( channels : word; { 1(单声)或者2(立体声) }resolution : word; { 8或者16,代表8位或16位声音 }
                  rate : longint; { 声音频率,如11025,22050, 44100}
                  fn : string { 对应的文件名称 } );
     //按键应答
     procedure SetKeyAcknowledge(value:boolean);
   published
     property RoomDetectStarted:boolean read FRoomDetectStarted write SetRoomDetectStart;
     //线路忙状态
     property LineBusy:boolean read FLineBusy write FLineBusy;
     //按键应答:自动叫班时,如果收不到叫班员的按键应答,则反复呼叫
     property KeyAcknowledged:boolean read FKeyAcknowledged write SetKeyAcknowledge;
   public
     Logined:boolean;
     LoginType:TLoginType;
     //登陆名,叫班员名称和编号
     LoginName:string;
     UserName:string;
     UserNumber:string;
     //添加或修改的类型
     EditType:TEditType;
     //一叫队列
     Queue:array [0..MAX_PLAN_NUM-1]of TCallRecord;
     QNum:integer;
     //手动叫班后的催叫队列
     RecallQ:array[0..MAX_RECALL_NUM-1]of TCallRecord;
     RecallNum:integer;
     //房间编号,仅在房间检测时使用,记录当前检测到的房间编号
     LineIndex:word;
     RoomGood:TRoomExistType;
     //人工呼叫录音时间,录音开始时初始化此计数器为0,之后每200ms加1,用于判断是否超过最长记录时间限制
     RecordTime:word;
     Player:TMediaPlayer;
     Recorder:TMediaPlayer;
     //呼叫计划表
     tbPlan:TADOTable;
     //叫班记录表
     tbCall:TADOTable;
     //电话录音表和人工呼叫录音表
     tbPhone,tbVoice:TADOTable;
     //房间编号表
     tbRoom:TADOTable;
     //临时表
     tbTemp:TADOTable;
     tbPlan2:TADOTable;
     //电话状态图标
     imPhone1,imPhone2:TBitBtn;
     //状态栏
     Status:TStatusPanel;
     procedure Execute;override;
     //从字符串中提取联叫房间号码
     procedure ExtractRoom(var CurRecord:TCallRecord;str:string);
     //程序启动时初始化自动叫班队列
     procedure InitQueue;
     //添加叫班计划时修改叫班队列
     procedure InsertQueue(InsertPlan:TCallRecord);
     //修改叫班队列项
     procedure ModifyQueue(NewPlan:TCallRecord;Number:integer);
     //打开链路
     procedure OpenLink(line:word;LinkState:TLinkState);
     //呼叫一条链路
     procedure CallOneLine(var CurRecord:TCallRecord;NowTime:TDateTime;Room:string; state:TCallState;isMain:boolean);
     //检测房间存在否
     function RoomExist(line:word):boolean;
     //开始录音
     procedure StartWaveRecord(fname:TFileName);
     //停止录音
     procedure StopWaveRecord;
   end;

var
  Caller:TAutoCaller;

function Out32(wAddr:word;bOut:byte):byte; stdcall; external 'inpout32.dll';
function Inp32(wAddr:word):byte; stdcall; external 'inpout32.dll';


implementation


procedure TAutoCaller.SetRoomDetectStart(value:boolean);
begin
  FRoomDetectStarted:=value;
  if(value) then begin
    LineBusy:=true;
    LineIndex:=0
  end
  else begin
    LineBusy:=false;
    OpenLink(0,lsReady);
  end;
end;

//按键应答
procedure TAutoCaller.SetKeyAcknowledge(value:boolean);
begin
  FKeyAcknowledged:=value;
end;

procedure TAutoCaller.CallOneLine(var CurRecord:TCallRecord;NowTime:TDateTime;Room:string; state:TCallState;isMain:boolean);
var
  j:integer;
  BKMark:TBookMark;
begin
        LineBusy:=true;  //线路忙
        if(state=csReady) then  begin
          if(SysSet.AutoCallEnabled)then
            Status.Text:='正在呼叫车次'+CurRecord.Train+' 房间'+Room+',请按键确认'
          else
            Status.Text:='提示,准备呼叫'+CurRecord.Train+' 房间'+Room+',请按键确认'
        end
        else
          Status.Text:='正在执行三催,车次'+CurRecord.Train+' 房间'+Room;
          //打开链路,如果没有启动自动叫班,那么将声音播放给叫班员
        if(SysSet.AutoCallEnabled or (state=csWaitSecond)) then
          OpenLink(CurRecord.line,lsAutoCall)
        else
          OpenLink(CurRecord.line,lsReplay);
	        //播放音频
	      KeyAcknowledged:=false;  //清除按键状态,准备检测新的键盘操作
        repeat
          if(state=csReady) then
            CurRecord.ActualFirstCall:=Now
          else
            CurRecord.ActualSecondCall:=Now;
          if(isMain) then begin
            //播放几秒钟的音乐
            if(SysSet.AutoCallEnabled) then begin
              Player.FileName:=RootDir+'\叫班音乐.wav';
              try
	              Player.Open;
	              Player.Play;
	              for j:=0 to 10*10-1 do begin     //最长等待10s
	                if(Player.Position=Player.Length) then
                    Break
	                else
	                  sleep(100);
	              end;
	              Player.Stop;
	              Player.Close;
              except
                showmessage(Player.ErrorMessage);
              end;
            end;
          end;
          //播放提示音
	        if(SysSet.AutoCallEnabled) then begin   //启动了自动呼叫
            if(state=csReady) then
              Player.FileName:=RootDir+'\AutoCall.wav'
            else
              Player.FileName:=RootDir+'\AutoReCall.wav';
          end
          else begin                              //没启动自动呼叫,仅提示
            if(state=csReady) then
              Player.FileName:=RootDir+'\PromptCall.wav'
            else
              Player.FileName:=RootDir+'\AutoReCall.wav';
          end;
          try
	          Player.Open;
	          Player.Play;
	          for j:=0 to 20*10-1 do begin     //最长等待20s
	            if(Player.Position=Player.Length) then
                Break
	            else
	              sleep(100);
	          end;
	          Player.Stop;
	          Player.Close;
          except
            showmessage(Player.ErrorMessage);
          end;
        until (KeyAcknowledged or SysSet.AutoCallEnabled or (state=csWaitSecond) or (not IsMain));
        //等待房间回话
        if(isMain) then begin
          if(SysSet.AutoCallEnabled or (state=csWaitSecond)) then begin
            Status.Text:='等待'+Room+'房间回话';
            //打开链路,记录10秒的录音
            OpenLink(CurRecord.line,lsRecord);
            if(state=csWaitSecond)then
              StartWaveRecord(GetWaveName(CurRecord.ActualSecondCall))
            else
              StartWaveRecord(GetWaveName(CurRecord.ActualFirstCall));
            sleep(10000);  //延迟10秒
            StopWaveRecord;
          end;
        end;
        //此次自动呼叫结束
        Status.Text:='';
        //呼叫完毕,转换到待用状态
        OpenLink(CurRecord.line,lsReady);
        LineBusy:=false;  //取消线路忙
	      //状态转换,如果是主叫房号,那么正常呼叫
        if(isMain) then begin
          if(state=csReady) then begin
            //添加叫班记录
            try
              tbCall.AppendRecord([Room,CurRecord.Train,
	              formatdatetime('hh:mm',CurRecord.TrainTime),trunc(Now)+CurRecord.PlanFirstCall,
                CurRecord.ActualFirstCall,'一叫']);
            except
            end;
          end
          else if(state=csWaitSecond) then begin
	          CurRecord.nState:=2;//csCalled;
            //添加叫班记录
            try
              tbCall.AppendRecord([CurRecord.Room,CurRecord.Train,
	              formatdatetime('hh:mm',CurRecord.TrainTime),trunc(Now)+CurRecord.time,
                CurRecord.ActualSecondCall,'三催']);
            except
            end;
          end;
        end
        //如果是联叫房号,那么
        else begin
          //添加叫班记录
          try
              tbCall.AppendRecord([Room,CurRecord.Train,
	              formatdatetime('hh:mm',CurRecord.TrainTime),trunc(Now)+CurRecord.PlanFirstCall,
                CurRecord.ActualFirstCall,'一叫']);
          except
          end;
        end;

        //如果超出了最大个数,那么删除首记录
        if(tbCall.RecordCount>=SysSet.MaxAutoWaveNum) then begin
            try
              tbCall.Last;
              tbCall.Prior;
              if(FileExists(GetWaveName(tbCall.FieldByName('实际一叫时间').AsDateTime))) then
                DeleteWaveFile(GetWaveName(tbCall.FieldByName('实际一叫时间').AsDateTime));
              tbCall.Delete;
              tbCall.First;
            except
            end;
        end;

end;
//判断是否呼叫完很久了,那么可以复位记录了
function IsResetStateTime(CallTime,NowTime:TDateTime):boolean;
begin
  //如果当前时间超过呼叫时间2个小时,那么准备将状态复位到0(Ready),返回真,否则返回假
  if ((NowTime-CallTime>2*3600*ONE_SECOND))
    or((NowTime<CallTime)and(NowTime+24*3600*ONE_SECOND-CallTime>2*3600*ONE_SECOND)) then
    Result:=True
  else
    Result:=False;
end;
//判断是否即将到达呼叫时间
function IsReadyTime(CallTime,NowTime:TDateTime):boolean;
begin
  //如果当前时间距离呼叫时间不到1分钟,那么返回真,否则假
  if ((NowTime>=CallTime)and(CallTime+24*3600*ONE_SECOND-NowTime<2*60*ONE_SECOND))
    or((NowTime<CallTime)and(CallTime-NowTime<2*60*ONE_SECOND)) then
    Result:=True
  else
    Result:=False;
end;
//判断是否到达了立即呼叫时间
function IsCallTime(CallTime,NowTime:TDateTime):boolean;
begin
  //如果当前时间在超过呼叫时间后的20分钟以内,那么返回真,否则假
  if ((NowTime>=CallTime)and(NowTime-CallTime<20*60*ONE_SECOND))
    or((NowTime<CallTime)and(NowTime+24*3600*ONE_SECOND-CallTime<20*60*ONE_SECOND)) then
    Result:=True
  else
    Result:=False;
end;

//根据数据库中的nState和当前时间关系,判断应进入何种状态
function GetCurrentState(nState:byte;CallTime,NowTime:TDateTime):TCallState;
var next_state:TCallState;
begin
  next_state:=csWaitFirst;
  case (nState)of
    //csReady
    0:if(IsResetStateTime(CallTime,NowTime))then  //正常情况下不会出现这个状态的,这里主要是为了容错
        next_state:=csWaitFirst
      else
        next_state:=csReady;
    //csWaitSecond
    1:if(IsResetStateTime(CallTime,NowTime))then  //正常情况下不会出现这个状态的,这里主要是为了容错
        next_state:=csWaitFirst
      else
        next_state:=csWaitSecond;
    //csCalled
    2:if(IsResetStateTime(CallTime,NowTime))then
        next_state:=csWaitFirst
      else
        next_state:=csCalled;
    //csWaitFirst,若到了准备呼叫时间或者由于某些事件的耽误,导致已经超过呼叫时间了,那么进入csReady状态
    3:if(IsReadyTime(CallTime,NowTime) or IsCallTime(CallTime,NowTime)) then
        next_state:=csReady
      else
        next_state:=csWaitFirst;
  end;
  Result:=next_state;
end;

procedure TAutoCaller.Execute;
var i,j,k:integer;
    NowTime,NowDate:TDateTime;
    LineState:byte;
    Phone1Started,Phone2Started:boolean;
    IsFirstCall:boolean;
    CurState:TCallState;
    CurRecord:TCallRecord;
    BKMark:TBookMark;
    ClrDelayTime:boolean;
    SleepCnt:integer;
begin
  //Suspend;
  sleep(3000);
  SleepCnt:=0;
  Phone1Started:=false;
  Phone2Started:=false;
  while(true) do begin
    //每6秒钟检查一次呼叫队列
    if(SleepCnt>2) then begin
      SleepCnt:=0;
      //下面处理一叫队列的初始化
      tbPlan2.First;
      //叫班队列不空且已经登陆,那么开始叫班
      while (not tbPlan2.Eof)and(Logined) do begin
        //检查链路状态,如果电话占线,则不能呼叫
	      LineState:=Inp32($379)and $30;
	      if(LineState<>$30) then begin  //录音电话工作中,占线
          Sleep(500);
	        Break;  //跳出循环,等待500ms后再查询队列
	      end;
        //
        CurRecord.enabled:=tbPlan2.FieldByName('开启').AsBoolean;
        try
          CurRecord.time:=StrToTime(tbPlan2.FieldByName('计划叫班时间').AsString);
          CurRecord.time:=CurRecord.time+strtoint(tbPlan2.FieldByName('晚点时间').AsString)*60*ONE_SECOND;
          CurRecord.time:=CurRecord.time-trunc(CurRecord.time); //去除整数部分
        except
        end;
        try
          CurRecord.RecNo:=tbPlan2.RecNo; //记录在数据表中的位置
          CurRecord.Room:=tbPlan2.FieldByName('房间号码').AsString;
          CurRecord.Train:=tbPlan2.FieldByName('车次').AsString;
          CurRecord.TrainTime:=strtotime(tbPlan2.FieldByName('开车时间').AsString);
          CurRecord.PlanFirstCall:=strtotime(tbPlan2.FieldByName('计划叫班时间').AsString);  //小时分钟秒
          CurRecord.nState:=tbPlan2.FieldByName('状态').AsInteger;
          if(not tbRoom.Locate('房间号码',CurRecord.Room,[])) then begin
            CurRecord.line:=1616;
          end
          else
            CurRecord.Line:=tbRoom.FieldByName('线路').AsInteger;
        except
        end;
        //获取联叫房间号码
        ExtractRoom(CurRecord,tbPlan2.FieldByName('联叫房号').AsString);
        //判断是否需要呼叫,nState -- CurState
        //    0--csReady,          即将到达呼叫时间,准备呼叫 ,颜色为红色
        //    1--csWaitSecond,     准备第二次呼叫,只要时间到了就可以立即呼叫,颜色为红色
        //    2--csCalled          刚刚呼叫过了,时间不超过两个小时,颜色为蓝色
        //    3--csWaitFirst       呼叫过两个小时以上了,等待重新呼叫,颜色为黑色
        NowTime:=Now-trunc(Now);
        ClrDelayTime:=false;  //默认为不清除晚点时间和联叫房号
        //判断当前应处于的状态
        CurState:=GetCurrentState(CurRecord.nState,CurRecord.time,NowTime);
        if((CurState=csReady)and IsCallTime(CurRecord.time,NowTime))
          or ((CurState=csWaitSecond)and IsCallTime(CurRecord.time+Sysset.CallInterval*60*ONE_SECOND,NowTime)) then begin
          //时间到了,呼叫
          CallOneLine(CurRecord,NowTime,CurRecord.Room,CurState,true);
          //呼叫联叫房间
          for k:=0 to CurRecord.StickyNum-1 do begin
            NowTime:=Now-trunc(Now);
            CallOneLine(CurRecord,NowTime,CurRecord.StickyRoom[k],csReady,false);
          end;
          //完成呼叫后重新设置当前状态
          if(SysSet.AutoReCallEnabled)and(CurState=csReady) then
              CurState:=csWaitSecond
          else
              CurState:=csCalled;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -