首先给出C#上位机程序:
using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;//线程类
using System.Windows.Forms;
namespace KRC2Server
{
public partial class Form1 : Form
{
//创建相关变量
public Thread MyThread;//线程1
public Thread thread;
public Socket WatchSocket;//服务端socket
public Socket clientSocket;//监听socket
public IPEndPoint LocalEP;//IP+端口
public IPAddress LocalIPv4;//获取当前IP的变量
public int DefaultPort;//下拉框的端口值1
public int DefaultPort2;//下拉框的端口值2
public int DefaultPort3;//下拉框的端口值3
public int SelectPort;//选择的下拉框
public int SaveNum;
private byte[] buffer = new byte[1024 * 1024 * 2];//接收到的消息的二进制字符串
private string TxtNum = "0";
private bool OpenORClose = false;
public int saveNum=0;
public int AutoSaveNum = 0;
//private byte[] BYte = new byte[8];
public Form1()
{
InitializeComponent();
}
//定义回调:解决跨线程访问问题
private delegate void SetTextValueCallBack(int Num, string strValue);
//声明回调
private SetTextValueCallBack setCallBack;
private void Form1_Load(object sender, EventArgs e)
{
//初始化列表框
string[] init_item = { "X","Y","Z", "EX", "EY", "EZ",
"axis1","axis2","axis3","axis4","axis5","axis6"};
for (int i = 0; i < 12; i++)
{
ListViewItem item = new ListViewItem(init_item[i]);
item.SubItems.Add("");
listView_kukapose.Items.Insert(i, item);
}
//获取本地IPv4地址
IPAddress LocalIpv4 = null;
//获取本机所有的IP地址列表
IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
//循环遍历所有IP地址
foreach (IPAddress IP in IpList)
{
//判断是否是IPv4地址
if (IP.AddressFamily == AddressFamily.InterNetwork)
{
LocalIpv4 = IP;
}
else
{
continue;
}
}
string LocalIP = Convert.ToString(LocalIpv4);
//TxtIP.AppendText(LocalIP);
TxtIP.Text = LocalIP;//textbox中显示当前的IP
LocalIPv4 = IPAddress.Parse(this.TxtIP.Text.Trim());//转换为IPAddress格式,并删除字符中句首和句末的空白格
DefaultPort = 59152;
DefaultPort2 = 59153;
DefaultPort3 = 59154;
TxtPort.Items.Add(Convert.ToString(DefaultPort));//下拉框显示
TxtPort.Items.Add(Convert.ToString(DefaultPort2));
TxtPort.Items.Add(Convert.ToString(DefaultPort3));
TxtPort.SelectedIndex = 0;
if(File.Exists(@"KukaPoseOrigin.txt"))
File.Delete(@"KukaPoseOrigin.txt");
if (File.Exists(@"AutoKukaPoseOrigin.txt"))
File.Delete(@"AutoKukaPoseOrigin.txt");
StartServer.Text = "启动服务器";
}
//******************************//
//启动服务器
//******************************//
private void StartServer_Click(object sender, EventArgs e)
{
StartListen();
}
private void StartListen()
{
try
{
//实例化套接字
WatchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//根据下拉框选择端口值
if (0 == TxtPort.SelectedIndex)
SelectPort = DefaultPort;
else if (1 == TxtPort.SelectedIndex)
SelectPort = DefaultPort2;
else if (2 == TxtPort.SelectedIndex)
SelectPort = DefaultPort3;
//创建网络端口,包括ip和端口
LocalEP = new IPEndPoint(LocalIPv4, SelectPort);
WatchSocket.Bind(LocalEP);
WatchSocket.Listen(10);
MyThread = new Thread(ListenClientConnect);//开启新线程去接收客户端
MyThread.Start();
textLog.AppendText( "服务器" + SelectPort.ToString() + "监听成功"+"\r\n");
StartServer.Text = "正在监听......";
//实例化回调
setCallBack = new SetTextValueCallBack(SetTextValue);
}
catch(Exception ex)
{
textLog.AppendText("服务器" + SelectPort.ToString() + "监听失败" + "\r\n");
//clientSocket.Shutdown(SocketShutdown.Both);
//clientSocket.Close();
return;
}
}
private void ListenClientConnect()
{
//while (true)
//{
try
{
while(true)
{
//Socket创建的新连接
Socket clientSocket = WatchSocket.Accept();
textLog.AppendText( "连接成功" + "\r\n");
//clientSocket.SendFile("连接成功");
clientSocket.Send(Encoding.UTF8.GetBytes("服务端发送消息:"));
//Thread thread = new Thread(ReceiveMessage);//开启新线程与客户端互相发送消息
thread = new Thread(ReceiveMessage);
thread.Start(clientSocket);
StartServer.BackColor = Color.Green;
StartServer.Enabled = false;
StartServer.Text = "已打开服务器-" + SelectPort.ToString();
}
}
catch (Exception ex)
{
//break;
//textLog.AppendText("服务器" + SelectPort.ToString() + "失去连接" + "\r\n");
}
//textLog.AppendText("服务器" + SelectPort.ToString() + "失去连接" + "\r\n");
//}
}
private void ReceiveMessage(object socket)
{
clientSocket = (Socket)socket;
while(true)
{
try
{
//获取从客户端发来的数据
//int length = clientSocket.Receive(buffer);//将消息接收到buffer中,length记录二进制的长度
string ip = LocalEP.ToString();
int size = clientSocket.Receive(buffer);
string StrMsg = Encoding.UTF8.GetString(buffer, 0, size);
if(StrMsg != "")
{
textLog.AppendText(ip + "Send" + "\r\n");
textLog.AppendText(StrMsg + "\r\n");//将消息显示在对话框中
}
//string StrReceiveMsg = Encoding.UTF8.GetString(buffer, 0, length);//将消息转成string
//if(StrReceiveMsg!="")
//{
if (OpenORClose==true)
{
if(StrMsg!="Flag1"&&StrMsg!="")
{
//string[] StrTmp = StrMsg.Split(',');
//listView_kukapose.Invoke(setCallBack, 0, StrTmp[0]);
//listView_kukapose.Invoke(setCallBack, 1, StrTmp[1]);
//listView_kukapose.Invoke(setCallBack, 2, StrTmp[2]);
//listView_kukapose.Invoke(setCallBack, 3, StrTmp[3]);
//listView_kukapose.Invoke(setCallBack, 4, StrTmp[4]);
//listView_kukapose.Invoke(setCallBack, 5, StrTmp[5]);
//listView_kukapose.Invoke(setCallBack, 6, StrTmp[6]);
//listView_kukapose.Invoke(setCallBack, 7, StrTmp[7]);
//listView_kukapose.Invoke(setCallBack, 8, StrTmp[8]);
//listView_kukapose.Invoke(setCallBack, 9, StrTmp[9]);
//listView_kukapose.Invoke(setCallBack, 10, StrTmp[10]);
//listView_kukapose.Invoke(setCallBack, 11, StrTmp[11]);
//KUKA Robot
string[] StrTmp = StrMsg.Split('\"');
listView_kukapose.Invoke(setCallBack, 0, StrTmp[1]);
listView_kukapose.Invoke(setCallBack, 1, StrTmp[3]);
listView_kukapose.Invoke(setCallBack, 2, StrTmp[5]);
listView_kukapose.Invoke(setCallBack, 3, StrTmp[11]);
listView_kukapose.Invoke(setCallBack, 4, StrTmp[9]);
listView_kukapose.Invoke(setCallBack, 5, StrTmp[7]);
listView_kukapose.Invoke(setCallBack, 6, StrTmp[13]);
listView_kukapose.Invoke(setCallBack, 7, StrTmp[15]);
listView_kukapose.Invoke(setCallBack, 8, StrTmp[17]);
listView_kukapose.Invoke(setCallBack, 9, StrTmp[19]);
listView_kukapose.Invoke(setCallBack, 10, StrTmp[21]);
listView_kukapose.Invoke(setCallBack, 11, StrTmp[23]);
}
else if(StrMsg=="Flag1")
{
AutoSaveNum += 1;
Txt_AutoNum.Text = Convert.ToString(AutoSaveNum);
//TxtNum = Convert.ToString(AutoSaveNum);
FileStream fs = new FileStream(@"AutoKukaPoseOrigin.txt", FileMode.Append);//采用FileMode.Append可以在txt中叠加而不覆盖,@加字符串可以代表不需要加转义字符
StreamWriter sw = new StreamWriter(fs);//输出流
float X = float.Parse(listView_kukapose.Items[0].SubItems[1].Text);//Parse表示将字符串转换为任意类型,这里转换为float
float Y = float.Parse(listView_kukapose.Items[1].SubItems[1].Text);
float Z = float.Parse(listView_kukapose.Items[2].SubItems[1].Text);
float EX = float.Parse(listView_kukapose.Items[3].SubItems[1].Text);
float EY = float.Parse(listView_kukapose.Items[4].SubItems[1].Text);
float EZ = float.Parse(listView_kukapose.Items[5].SubItems[1].Text);
double Ex = EX * Math.PI / 180;
double Ey = EY * Math.PI / 180;
double Ez = EZ * Math.PI / 180;
sw.Write(Convert.ToString(AutoSaveNum) + "," +
" " + X.ToString("#0.000000") +
"," + Y.ToString("#0.000000") +
"," + Z.ToString("#0.000000") +
"," +
" " + Ex.ToString("#0.000000") +
"," + Ey.ToString("#0.000000") +
"," + Ez.ToString("#0.000000") +
"\r\n");
sw.Close();
fs.Close();
}
}
else
{
//string[] StrTmps = StrMsg.Split(',');
//listView_kukapose.Invoke(setCallBack, 0, StrTmps[0]);
//listView_kukapose.Invoke(setCallBack, 1, StrTmps[1]);
//listView_kukapose.Invoke(setCallBack, 2, StrTmps[2]);
//listView_kukapose.Invoke(setCallBack, 3, StrTmps[3]);
//listView_kukapose.Invoke(setCallBack, 4, StrTmps[4]);
//listView_kukapose.Invoke(setCallBack, 5, StrTmps[5]);
//listView_kukapose.Invoke(setCallBack, 6, StrTmps[6]);
//listView_kukapose.Invoke(setCallBack, 7, StrTmps[7]);
//listView_kukapose.Invoke(setCallBack, 8, StrTmps[8]);
//listView_kukapose.Invoke(setCallBack, 9, StrTmps[9]);
//listView_kukapose.Invoke(setCallBack, 10, StrTmps[10]);
//listView_kukapose.Invoke(setCallBack, 11, StrTmps[11]);
//KUKA Robot
string[] StrTmp = StrMsg.Split('\"');
listView_kukapose.Invoke(setCallBack, 0, StrTmp[1]);
listView_kukapose.Invoke(setCallBack, 1, StrTmp[3]);
listView_kukapose.Invoke(setCallBack, 2, StrTmp[5]);
listView_kukapose.Invoke(setCallBack, 3, StrTmp[11]);
listView_kukapose.Invoke(setCallBack, 4, StrTmp[9]);
listView_kukapose.Invoke(setCallBack, 5, StrTmp[7]);
listView_kukapose.Invoke(setCallBack, 6, StrTmp[13]);
listView_kukapose.Invoke(setCallBack, 7, StrTmp[15]);
listView_kukapose.Invoke(setCallBack, 8, StrTmp[17]);
listView_kukapose.Invoke(setCallBack, 9, StrTmp[19]);
listView_kukapose.Invoke(setCallBack, 10, StrTmp[21]);
listView_kukapose.Invoke(setCallBack, 11, StrTmp[23]);
}
// }
//Console.WriteLine("接收客户端{0},消息{1}", clientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(buffer, 0, length));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
//textLog.AppendText("服务器" + SelectPort.ToString() + "失去连接" + "\r\n");
break;
}
//textLog.AppendText("服务器" + SelectPort.ToString() + "失去连接" + "\r\n");
}
textLog.AppendText("服务器" + SelectPort.ToString() + "失去连接" + "\r\n");
}
private void SetTextValue(int Num, string strValue)
{
float Pose = float.Parse(strValue);
listView_kukapose.Items[Num].SubItems[1].Text = Pose.ToString("#0.000000");
}
private void Form1_FormClose(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("您确定要注销登录吗?", "KRC4Server", MessageBoxButtons.YesNo) == DialogResult.Yes)//中文版本
{
//关闭socket避免占用端口
if (clientSocket != null)
{
WatchSocket.Close();
clientSocket.Close();
MyThread.Abort();
thread.Abort();
clientSocket = null;
}
//关闭线程
//MyThread.Abort();
//SocketServer = null;
e.Cancel = false;
}
else e.Cancel = true;
}
// 检查一个Socket是否可连接
//private bool IsSocketConnected(Socket client)
//{
// bool blockingState = client.Blocking;
// try
// {
// byte[] tmp = new byte[1];
// client.Blocking = false;
// client.Send(tmp, 0, 0);
// return false;
// }
// catch (SocketException e)
// {
// // 产生 10035 == WSAEWOULDBLOCK 错误,说明被阻止了,但是还是连接的
// if (e.NativeErrorCode.Equals(10035))
// return false;
// else
// return true;
// }
// finally
// {
// client.Blocking = blockingState; // 恢复状态
// }
//}
private void button1_Click(object sender, EventArgs e)
{
if ("" == listView_kukapose.Items[0].SubItems[1].Text)
{
MessageBox.Show("未接收到位姿,无法保存!", "提示");
}
else
{
saveNum += 1;
textBox_num.Text = Convert.ToString(saveNum);
TxtNum = Convert.ToString(saveNum);
FileStream fs = new FileStream(@"KukaPoseOrigin.txt", FileMode.Append);//采用FileMode.Append可以在txt中叠加而不覆盖,@加字符串可以代表不需要加转义字符
StreamWriter sw = new StreamWriter(fs);//输出流
float X = float.Parse(listView_kukapose.Items[0].SubItems[1].Text);//Parse表示将字符串转换为任意类型,这里转换为float
float Y = float.Parse(listView_kukapose.Items[1].SubItems[1].Text);
float Z = float.Parse(listView_kukapose.Items[2].SubItems[1].Text);
float EX = float.Parse(listView_kukapose.Items[3].SubItems[1].Text);
float EY = float.Parse(listView_kukapose.Items[4].SubItems[1].Text);
float EZ = float.Parse(listView_kukapose.Items[5].SubItems[1].Text);
double Ex = EX * Math.PI / 180;
double Ey = EY * Math.PI / 180;
double Ez = EZ * Math.PI / 180;
sw.Write(TxtNum + "," +
" " + X.ToString("#0.000000") +
"," + Y.ToString("#0.000000") +
"," + Z.ToString("#0.000000") +
"," +
" " + Ex.ToString("#0.000000") +
"," + Ey.ToString("#0.000000") +
"," + Ez.ToString("#0.000000") +
"\r\n");
sw.Close();
fs.Close();
}
}
private void button2_Click(object sender, EventArgs e)
{
if(OpenORClose==false)
{
OpenORClose = true;
button2.Text = "取消连续";
}
else if (OpenORClose == true)
{
OpenORClose = false;
button2.Text = "连续保存";
}
}
}
}
接下来将具体举出几个值得注意的地方
1.在创建服务端监听和接收时,按照规范应该采用多线程进行通讯,如下面代码展示了服务端创建以及采用新的线程去监听。其中Mythread为新的线程
private void StartListen()
{
try
{
//实例化套接字
WatchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//根据下拉框选择端口值
if (0 == TxtPort.SelectedIndex)
SelectPort = DefaultPort;
else if (1 == TxtPort.SelectedIndex)
SelectPort = DefaultPort2;
else if (2 == TxtPort.SelectedIndex)
SelectPort = DefaultPort3;
//创建网络端口,包括ip和端口
LocalEP = new IPEndPoint(LocalIPv4, SelectPort);
WatchSocket.Bind(LocalEP);
WatchSocket.Listen(10);
MyThread = new Thread(ListenClientConnect);//开启新线程去接收客户端
MyThread.Start();
textLog.AppendText( "服务器" + SelectPort.ToString() + "监听成功"+"\r\n");
StartServer.Text = "正在监听......";
//实例化回调
setCallBack = new SetTextValueCallBack(SetTextValue);
}
catch(Exception ex)
{
textLog.AppendText("服务器" + SelectPort.ToString() + "监听失败" + "\r\n");
//clientSocket.Shutdown(SocketShutdown.Both);
//clientSocket.Close();
return;
}
}
2.设置的Flag1可以自行替换成其他标志,其作用是kuka发过来进行判断该进行位置保存。
下面是上位机的样子
其中包含手动保存和自动保存模块,后面显示目前保存的坐标点数,其存在两个txt中,分别为:AutoKukaPoseOrigin.txt&KukaPoseOrigin.txt,具体见下图
下面是对应的KUKA的SRC和XML程序:
SRC:
&ACCESS RVP
&PARAM EDITMASK = *
&PARAM TEMPLATE = C:\KRC\Roboter\Template\ExpertVorgabe
DEF TYM()
;FOLD Declaration
INT i
DECL EKI_STATUS RET
CHAR valueChar[256]
CHAR Bytes1[64]
INT valueInt
BOOL valueBOOL
FRAME valueFrame
$FLAG[1]=FALSE
$FLAG[2]=FALSE
BAS(#TOOL,0)
BAS(#BASE,0)
;ENDFOLD (Declaration)
;FOLD Communicated data
;FOLD Receive from external program
; <Sensor>
; <Message>Example message</Message>
; <Positions>
; <Current X="4645.2" />
; <Before>
; <X>0.9842</X>
; </Before>
; </Positions>
; <Nmb>8</Nmb>
; <Status>
; <IsActive>1</IsActive>
; </Status>
; <Read>
; <xyzabc X="210.3" Y="825.3" Z="234.3" A="84.2" B="12.3" C="43.5" />
; </Read>
; <Show error="0" temp="9929">Taginfo in attributes</Show>
; <Free>2912</Free>
; </Sensor>
;ENDFOLD (Receive from external program)
;FOLD Send to external program
; <Robot>
; <Data>
; <ActPos X="1000.12">
; </ActPos>
; <LastPos A="..." B="..." C="..." X="..." Y="..." Z="...">
; </LastPos>
; </Data>
; <Mode>ConnectSensor</Mode>
; <RobotLamp>
; <GrenLamp>
; <LightOn>1</LightOn>
; </GrenLamp>
; </RobotLamp>
; <Status>12345678</Status>
; </Robot>
;ENDFOLD (Send to external program)
;ENDFOLD (Communicated data)
RET=EKI_Init("XmlTYM")
RET=EKI_Open("XmlTYM")
;FOLD Write data to connection
; Write frame to <LastPos X="" Y="" Z="" A="" B="" C="" />这是将控制器中的值写入结构变量中
RET=EKI_SetFrame("XmlTYM","Robot/Data/Pos", $POS_ACT)
; Write real to <Axis ="" />
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A1", $AXIS_ACT.A1)
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A2", $AXIS_ACT.A2)
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A3", $AXIS_ACT.A3)
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A4", $AXIS_ACT.A4)
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A5", $AXIS_ACT.A5)
RET=EKI_SetReal("XmlTYM","Robot/Data/Axis/@A6", $AXIS_ACT.A6)
; Write int to <Status></Status>
RET=EKI_SetInt("XmlTYM","Robot/Status", 12345678)
; Write string to <Mode></Mode>
RET=EKI_SetString("XmlTYM","Robot/Mode","ConnectSensor")
; Write bool to <LightOn></LightOn>
RET=EKI_SetBool("XmlTYM","Robot/RobotLamp/GrenLamp/LightOn",true);XmlTYM是通道名称,Robot/RobotLamp/GrenLamp/LightOn是结构中的位置名称,true是写入存储器的值
;ENDFOLD (Write data to connection)
RET = EKI_Send("XmlTYM","Robot")
WAIT SEC 0.1
loop
Bytes1[]="Flag1"
RET = EKI_Send("XmlTYM",Bytes1[])
WAIT FOR $FLAG[2]
wait sec 2
$flag[2]=false
endloop
FOR i=(1) TO (256)
valueChar[i]=0
ENDFOR
valueInt=0
valueBOOL=FALSE
valueFrame={X 0.0,Y 0.0,A 0.0,B 0.0,C 0.0}
;wait until data read
WAIT FOR $FLAG[998]
;FOLD Get received sensor data
; Get string in <Message>Example message</Message>
RET=EKI_GetString("XmlTYM","Sensor/Message",valueChar[])
; Get int value in <Nmb>8</Nmb>
RET=EKI_GetInt("XmlTYM","Sensor/Nmb",valueInt)
; Get bool value in textnode <IsActive>1</IsActive>
RET=EKI_GetBool("XmlTYM","Sensor/Status/IsActive" ,valueBOOL)
; Get frame in <xyzabc X="210.3" Y="825.3" Z="234.3" A="84.2" B="12.3" C="43.5" />
RET=EKI_GetFrame("XmlTYM","Sensor/Read/xyzabc",valueFrame)
;ENDFOLD (Get received sensor data)
RET=EKI_Close("XmlTYM")
RET=EKI_Clear("XmlTYM")
END
XML:
<ETHERNETKRL>
<CONFIGURATION>
<EXTERNAL>
<IP>192.168.125.2</IP>
<PORT>59152</PORT>
</EXTERNAL>
</CONFIGURATION>
<RECEIVE>
<XML>
<ELEMENT Tag="Sensor/Message" Type="STRING"/>
<ELEMENT Tag="Sensor/Nmb" Type="INT"/>
<ELEMENT Tag="Sensor/Status/IsActive" Type="BOOL"/>
<ELEMENT Tag="Sensor/Read/xyzabc" Type="FRAME"/>
<ELEMENT Tag="Sensor/Fre" Set_Flag="998"/>
</XML>
<RAW>
<ELEMENT Tag="Buffer" Type="STREAM" Set_Flag="2" EOS="13,10"/>
</RAW>
</RECEIVE>
<SEND>
<XML>
;下面是发送数据的数据结构
<ELEMENT Tag="Robot/Data/Pos/@X"/>
<ELEMENT Tag="Robot/Data/Pos/@Y"/>
<ELEMENT Tag="Robot/Data/Pos/@Z"/>
<ELEMENT Tag="Robot/Data/Pos/@A"/>
<ELEMENT Tag="Robot/Data/Pos/@B"/>
<ELEMENT Tag="Robot/Data/Pos/@C"/>
<ELEMENT Tag="Robot/Data/Axis/@A1"/>
<ELEMENT Tag="Robot/Data/Axis/@A2"/>
<ELEMENT Tag="Robot/Data/Axis/@A3"/>
<ELEMENT Tag="Robot/Data/Axis/@A4"/>
<ELEMENT Tag="Robot/Data/Axis/@A5"/>
<ELEMENT Tag="Robot/Data/Axis/@A6"/>
<ELEMENT Tag="Robot/Status"/>
;下面两行是为了发送数据所配置的XML结构
<ELEMENT Tag="Robot/Mode"/>
<ELEMENT Tag="Robot/RobotLamp/GrenLamp/LightOn"/>
</XML>
</SEND>
</ETHERNETKRL>