样利用局域网在大屏幕上显示体育比赛成绩
|
随着中国申奥成功,我国许多大中城市都在建设自己的体育中心(或体育场)。应当说这些场和馆的许多设计和设施,起点都是很高的。比如新落成的四万座“烟台中心体育场”,不仅有自己独立的局域网、大屏幕显示,而且有先进的“田、径”比赛记分的裁判系统。这些硬件设施,施工时往往是各大公司各自完成,所以整个工程完工后,如何使这些硬件用软件联系起来,使之协调工作,这就是体育中心自己的事了。本文的作者,自始至终参入了这项工作,略有所得,也走过一些弯路,现把成功的东西写下来,愿与同行交流。 烟台中心体育场的硬件系统框图如下: 记分裁判系统 局域网 大屏幕显示系统
(由深圳菲普公司提供) (由广东信息工程施工) (由北京利亚德施工)
图 一
图一中,记分裁判系统、大屏幕显示系统的软件部分都是自己本公司开发的。(记分裁判系统平台是WindowsNT;大屏幕显示系统平台是Windows98),两者连接的中心环节硬件则是局域网,软件需自己编写。 本文作者用Delphi5在Windows2K平台编写,已调试通过。具体做法如下: 1.按图2设计界面。
图 二
图二中使用了Delphi中两个重要组件,ServerSoket 和ClientSoket 。这两个组件位于VCL面板的 Internet 页上。它们是 Delphi 为实现 TCP/IP 协议,封装了 Windows 提供的一组名为 WinSock 的 API函数。所以设计者不必知道TCP/IP 的细节,只要简单的说明: Host - 服务器的域名; Address - 服务器的IP地址;(在运行时确定) Port - 指定应用程序使用的端口;(在设计时确定) Service - 服务器名称字符串, 就可以了。 图二界面全部程序如下: unit main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls, Buttons, ScktComp, ExtCtrls, ComCtrls; type TChatForm = class(TForm) MainMenu1: TMainMenu; File1: TMenuItem; Exit1: TMenuItem; FileConnectItem: TMenuItem; FileListenItem: TMenuItem; StatusBar1: TStatusBar; Bevel1: TBevel; Panel1: TPanel; Memo1: TMemo; Memo2: TMemo; N1: TMenuItem; SpeedButton1: TSpeedButton; Disconnect1: TMenuItem; ServerSocket: TServerSocket; ClientSocket: TClientSocket; BitBtn1: TBitBtn; procedure FileListenItemClick(Sender: TObject); procedure FileConnectItemClick(Sender: TObject); procedure Exit1Click(Sender: TObject); procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormCreate(Sender: TObject); procedure ServerSocketError(Sender: TObject; Number: Smallint; var Description: string; Scode: Integer; const Source, HelpFile: string; HelpContext: Integer; var CancelDisplay: Wordbool); procedure Disconnect1Click(Sender: TObject); procedure ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocketAccept(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure BitBtn1Click(Sender: TObject); protected IsServer: Boolean; end; var ChatForm: TChatForm; Server: String; implementation uses Unit1; {$R *.DFM} procedure TChatForm.FileListenItemClick(Sender: TObject); begin FileListenItem.Checked := not FileListenItem.Checked; if FileListenItem.Checked then begin ClientSocket.Active := False; ServerSocket.Active := True; Statusbar1.Panels[0].Text := 'Listening...'; end else begin if ServerSocket.Active then ServerSocket.Active := False; Statusbar1.Panels[0].Text := ''; end; end; procedure TChatForm.FileConnectItemClick(Sender: TObject); begin if ClientSocket.Active then ClientSocket.Active := False; if InputQuery('Computer to connect to', 'Address Name:', Server) then if Length(Server) > 0 then with ClientSocket do begin Host := Server; Active := True; FileListenItem.Checked := False; end; end; procedure TChatForm.Exit1Click(Sender: TObject); begin ServerSocket.Close; ClientSocket.Close; Close; end; procedure TChatForm.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_Return then if IsServer then ServerSocket.Socket.Connections[0].SendText(Memo1.Lines[Memo1.Lines.Count - 1]) else ClientSocket.Socket.SendText(Memo1.Lines[Memo1.Lines.Count - 1]); end;
procedure TChatForm.FormCreate(Sender: TObject); begin FileListenItemClick(nil); end; procedure TChatForm.ServerSocketError(Sender: TObject; Number: Smallint; var Description: string; Scode: Integer; const Source, HelpFile: string; HelpContext: Integer; var CancelDisplay: Wordbool); begin ShowMessage(Description); end; procedure TChatForm.Disconnect1Click(Sender: TObject); begin ClientSocket.Active := False; ServerSocket.Active := True; Statusbar1.Panels[0].Text := 'Listening...'; end; procedure TChatForm.ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); begin Statusbar1.Panels[0].Text := 'Connected to: ' + Socket.RemoteHost; end; procedure TChatForm.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Add(Socket.ReceiveText); end; procedure TChatForm.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Add(Socket.ReceiveText); end; procedure TChatForm.ServerSocketAccept(Sender: TObject; Socket: TCustomWinSocket); begin IsServer := True; Statusbar1.Panels[0].Text := 'Connected to: ' + Socket.RemoteAddress; end; procedure TChatForm.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Clear; end; procedure TChatForm.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin FileListenItemClick(nil); end; procedure TChatForm.ClientSocketError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin Memo2.Lines.Add('Error connecting to : ' + Server); ErrorCode := 0; end; procedure TChatForm.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin Statusbar1.Panels[0].Text := 'Listening...'; end; procedure TChatForm.BitBtn1Click(Sender: TObject); begin form1.show; end; end. 2.代码重点部分说明 ServerSocket 和 ClientSocket 有一个共同的属性 Active,它用来控制组件是否处于工作状态。当ServerSocket 设置为True 时,服务器端开始监听是否有客户要与之连接。当ClintSocket 设置为True时,客户端准备与服务器端连接。在上述代码中: procedure TChatForm.FileListenItemClick(Sender: TObject); begin FileListenItem.Checked := not FileListenItem.Checked; if FileListenItem.Checked then begin ClientSocket.Active := False; ServerSocket.Active := True; Statusbar1.Panels[0].Text := 'Listening...'; end else begin if ServerSocket.Active then ServerSocket.Active := False; Statusbar1.Panels[0].Text := ''; end; end; 是这样的: 如果Listen菜单有复选标记,用户单击它后取消复选标记,关闭服务器,打开客户端,将这个窗口作为客户程序来运行。如果Listen菜单没有复选标记,用户单击它后标上复选标记,打开服务器,关闭客户端,将这个窗口作为服务器程序来运行。同时界面状态栏的文字,提醒用户处于的状态。 以下代码: procedure TChatForm.FileConnectItemClick(Sender: TObject); begin if ClientSocket.Active then ClientSocket.Active := False; if InputQuery('Computer to connect to', 'Address Name:', Server) then if Length(Server) > 0 then with ClientSocket do begin Host := Server; Active := True; FileListenItem.Checked := False; end; end; 的作用是这样的: 这个菜单项被触发时,首先关闭 ClientSocket 。然后调用 InputQuery 函数,询问用户要连接服务器的IP地址,用户正确输入后,将新的IP值付给 ClientSocket 的 Address Name 属性,同时设置 Active 为 True ,让它用新的IP地址与指定的服务器连接。
3.图二界面的作用 记分裁判系统送出的比赛数据,是按标准 IP/TCP 格式-“ START,……,END ”送出的。下面是一组具体的例子: START,男子组100米决赛1组,4,1,4,90029,刘子雨,,11.22,2,6,90028,刘英力,,11.43,3,3,90032,刘英男,,11.49,4,5,90019,周淑华,,11.57,END 上面各组数据是用“,”分开的,它们是这样约定的: 第一个分号前,“ START ” 格式头 第二个分号前,“ 男子组100米决赛1组 ” 组别 第三个分号前,“ 4 ” 参加人数 第四个分号前,“ 1 ” 第一名 第五个分号前,“ 4 ” 第一名所在跑道 第六个分号前,“ 90029 ” 第一名编号 第七个分号前,“ 刘子雨 ” 第一名姓名 第九个分号前,“ 11.22 ” 第一名成绩 (注:前面夹一分号) 第十个分号前,“ 6 ” 第二名所在跑道 . . . (共四组这样的数据) END 格式尾
按装在大屏幕主机上“ 界面二”,用菜单项 “ Listen ” 置为监听状态,做服务器端;按装在裁判系统主机上“ 界面二”,用菜单项 “Listen ”置为客户机状态,做客户端。并随时准备与“ 连接键”联用,给出 IP 地址,与服务器连接。 界面二中 Memo2 的是为接受裁判系统送来的数据而设计的。它的WordWrap 属性为 False 。这样它接受到数据,无论多长,它都是一行数据。这为以后从Memo2剥离数据打下基础。 至于界面二中的其他组件,读者看一下程序,不需赘述自明。 4.剥离数据的中间界面 前面我们说过,裁判系统送来的是一组“ START …… END ”数据,它里面除了我们有用的数据外,还有一些格式分界用的代码,象“START”;“END”;“,” 等等。我们必须从“ START …… END ”数据中,分离出我们所要显示的数据。而不必理会不需显示的数据。下面“图 三”即是为这个目的而设计的。
图 三
按一下“模拟”键(这是为调机设计的),会触发 OpenDaiglog1 对话框,询问输入“ START …… END ”数据(本文电子版提供两组这样的数据,调试者可事先复制到机器的一个目录中)。当完成OpenDaiglog1 对话框操作后,会在图二的 Memo2 中出现这组数据。 当再按“显示”键时,程序会要分两步走: ★:第一步,将图三显示成“图四”
图 四 从上图中可以看出,Memo2 的每一行是以“,”为“断点”,阶梯式显示“ START …… END ”内容。Memo3 的每一行,是对应 Memo2 最后一个“,”所在的位置(左第一个为“1”)。Memo4 是已剥离出带“,”的数据。
完成以上功能,只需以下代码: procedure TForm1.BitBtn1Click(Sender: TObject); var s,s1,s2:string; i,j,m,n,k,g:integer;
begin s:=chatform.memo2.Text ; j:=length(s); for i:=1 to j do begin if s[i]=',' then begin s1:=copy(s,-i,i); j:=length(s1); memo2.Lines .add(s1); memo3.Lines .add(inttostr(j)); end ; end ; for g:=0 to memo3.Lines.Count-2 do begin m:= strtoint(trim(memo3.lines[g]));
n:= strtoint(trim(memo3.lines[g+1])); k:=n-m; memo4.Lines.add(copy(memo2.Lines [g+1],m,k)) ; end;
为了最终将比赛成绩,在Memo2、Memo3、Memo4 上覆盖了一个 panel1 如图五 (运行是时,实际看不到Memo2、Memo3、Memo4的内容)。
图 五 ★ :第二步,将图五显示成图六。
图 六 以上两步的完整程序如下: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons; type TForm1 = class(TForm) BitBtn1: TBitBtn; Edit1: TEdit; Memo4: TMemo; Memo2: TMemo; Memo3: TMemo; Panel1: TPanel; Label9: TLabel; Label2: TLabel; Label30: TLabel; Label3: TLabel; Label14: TLabel; Label10: TLabel; Label11: TLabel; Label12: TLabel; Label13: TLabel; Label4: TLabel; Label5: TLabel; Label6: TLabel; Label7: TLabel; Label8: TLabel; Label19: TLabel; Label15: TLabel; Label16: TLabel; Label17: TLabel; Label18: TLabel; Label24: TLabel; Label23: TLabel; Label22: TLabel; Label27: TLabel; Label26: TLabel; Label25: TLabel; Label20: TLabel; Label21: TLabel; Label28: TLabel; Label29: TLabel; OpenDialog1: TOpenDialog; Button2: TButton; Label1: TLabel; Label31: TLabel; Label32: TLabel; procedure BitBtn1Click(Sender: TObject); // procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Edit1DblClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation uses main; {$R *.DFM} procedure TForm1.BitBtn1Click(Sender: TObject); var s,s1,s2:string; i,j,m,n,k,g:integer; begin s:=chatform.memo2.Text ; j:=length(s); for i:=1 to j do begin if s[i]=',' then begin s1:=copy(s,-i,i); j:=length(s1); memo2.Lines .add(s1); memo3.Lines .add(inttostr(j)); end ; end ; for g:=0 to memo3.Lines.Count-2 do begin m:= strtoint(trim(memo3.lines[g])); n:= strtoint(trim(memo3.lines[g+1])); k:=n-m; memo4.Lines.add(copy(memo2.Lines [g+1],m,k)) ; end; label2.Caption :=copy (memo4.Lines[2],2,60); //类别 label3.Caption :=copy (memo4.Lines[3],2,2); //人数 //label4.Caption :=copy (memo4.Lines[4],2,2); //名次 label5.Caption :=copy (memo4.Lines[5],2,2); //跑道 -1 label6.Caption :=copy (memo4.Lines[6],2,5); //编号 -1 label7.Caption :=copy (memo4.Lines[7],2,6); //姓名 -1 label8.Caption :=copy (memo4.Lines[9],2,6); //成绩 -1 label15.Caption :=copy (memo4.Lines[11],2,2); //跑道 -2 label16.Caption :=copy (memo4.Lines[12],2,5); //编号 -2 label17.Caption :=copy (memo4.Lines[13],2,6); //姓名 -2 label18.Caption :=copy (memo4.Lines[15],2,6); //成绩 -2 label21.Caption :=copy (memo4.Lines[17],2,2); //跑道 -3 label22.Caption :=copy (memo4.Lines[18],2,5); //编号 -3 label23.Caption :=copy (memo4.Lines[19],2,6); //姓名 -3 label24.Caption :=copy (memo4.Lines[21],2,6); //成绩 -3 label26.Caption :=copy (memo4.Lines[23],2,2); //跑道 -4 label27.Caption :=copy (memo4.Lines[24],2,5); //编号 -4 label28.Caption :=copy (memo4.Lines[25],2,6); //姓名 -4 label29.Caption :=copy (memo4.Lines[27],2,6); //成绩 -4 panel1.Visible :=true; end; procedure TForm1.Button2Click(Sender: TObject); var F: Textfile; S:string; begin memo2.Lines.Clear ; memo2.Lines.Add('0'); memo3.Lines.Clear ; memo3.Lines.Add('0'); memo4.Lines.Clear ; memo4.Lines.Add('0'); OpenDialog1.Execute; AssignFile(F, OpenDialog1.FileName); Reset(F); Readln(F, S); { Read the first line out of the file } chatform.memo2.Text := S; { Put string in a TEdit control } CloseFile(F); end; procedure TForm1.Edit1DblClick(Sender: TObject); begin label1.Caption :=edit1.Text ; end; end. procedure TForm1.Button1Click(Sender: TObject); var F: Textfile; S:string; begin memo2.Lines.Clear ; memo2.Lines.Add('0'); memo3.Lines.Clear ; memo3.Lines.Add('0'); memo4.Lines.Clear ; memo4.Lines.Add('0'); OpenDialog1.Execute; AssignFile(F, OpenDialog1.FileName); Reset(F); Readln(F, S); { Read the first line out of the file } form1.memo2.Text := S; { Put string in a TEdit control } CloseFile(F); end; end.最后应当说明的是,我们所设计的界面二,不但可以显示体育比赛成绩,还可以在比赛中,随时与有联系的其他客户端通信,这些读者稍看一下程序,很容易看出。 |