📄 myclass.pas
字号:
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 + -