在通常情况下AGV的开发是使用PLC或者单片机,我们会使用MOXA等串口服务器配置485或者232通信。
我们会将AGV的MOXA设备配置成服务端。我们按照一定的数据协议从PLC或者单片机获得AGV的信息。
今天我抛开这些协议,通过工具模拟实现openTCS的Socket通信。
我这边用winform写了一个简单的Socket服务端工具(超简单那种)
我们的代码是基于第一篇文章的,具体参照 https://blog.csdn.net/jielounlee/article/details/85985715
那接下来,我们来具体操作一下
一、在地图上设置AGV的IP地址与端口号
每辆小车都要加上IP和Port。我这儿就将其他车辆删掉了,然后保存一下
二、在第一篇文章的TestCommAdapter中写上具体通信
我们首先写一个Socket工具类
package org.opentcs.testvehicle;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
*
* @author zjw
*/
public class SocketUtils {
private String ip;
private int port;
public SocketUtils(String ip, int port) {
this.ip = ip;
this.port = port;
}
public SocketUtils(String ip, String port) {
this.ip = ip;
try {
this.port = Integer.parseInt(port);
}
catch (Exception ex) {
this.port = 4001;
}
}
public String send(String msg) {
Socket socket = null;
String ret = "";
try {
socket = new Socket(ip, port);
socket.setSoTimeout(1000);
OutputStream out = socket.getOutputStream();
out.write(msg.getBytes());
InputStream in = socket.getInputStream();
byte[] buf = new byte[4096];
int len = in.read(buf);
ret = new String(buf, 0, len);
out.close();
in.close();
socket.close();
return ret;
}
catch (Exception ex) {
try {
socket.close();
}
catch (Exception e) {
}
return "";
}
finally {
try {
socket.close();
}
catch (Exception e) {
}
}
}
public byte[] send(byte[] bytes) {
Socket socket = null;
try {
socket = new Socket(ip, port);
socket.setSoTimeout(500);
OutputStream out = socket.getOutputStream();
out.write(bytes);
InputStream in = socket.getInputStream();
byte[] buf = new byte[8];
int len = in.read(buf);
out.close();
in.close();
socket.close();
return buf;
}
catch (Exception ex) {
try {
socket.close();
}
catch (Exception e) {
}
return null;
}
finally {
try {
socket.close();
}
catch (Exception e) {
}
}
}
}
在TestCommAdapter的构造方法中实例化SocketUtils
在选择AGV适配器的时候会获取AGV的位置,显示在画面上,开启线程
停用的时候将线程也停止了
接下来就是在线程中处理逻辑了
我这边比较简单,仅仅只是模拟了获取状态以及移动操作。
具体完整Adapter代码如下:
package org.opentcs.testvehicle;
import com.google.inject.assistedinject.Assisted;
import java.util.List;
import static java.util.Objects.requireNonNull;
import javax.inject.Inject;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.Route.Step;
import org.opentcs.drivers.vehicle.BasicVehicleCommAdapter;
import org.opentcs.drivers.vehicle.MovementCommand;
import org.opentcs.util.CyclicTask;
import org.opentcs.util.ExplainedBoolean;
import org.opentcs.data.model.Vehicle.Orientation;
/**
*
* @author zjw
*/
public class TestCommAdapter extends BasicVehicleCommAdapter {
private final SocketUtils socketUtils;
private TestAdapterComponentsFactory componentsFactory;
private Vehicle vehicle;
private boolean initialized;
private CyclicTask testTask;
@Inject
public TestCommAdapter(TestAdapterComponentsFactory componentsFactory, @Assisted Vehicle vehicle) {
super(new TestVehicleModel(vehicle), 2, 1, "CHARGE");
this.componentsFactory = componentsFactory;
this.vehicle = vehicle;
String ip = vehicle.getProperty("IP");
String port = vehicle.getProperty("Port");
this.socketUtils = new SocketUtils(ip, port);
}
@Override
public void initialize() {
initialized = true;
//网络通信,获取当前位置,电量,等信息
//getProcessModel().setVehicleState(Vehicle.State.IDLE);
//getProcessModel().setVehiclePosition("Point-0001");
}
@Override
public synchronized void enable() {
if (isEnabled()) {
return;
}
//开启线程(略)
testTask = new TestTask();
Thread simThread = new Thread(testTask, getName() + "-Task");
simThread.start();
super.enable();
}
@Override
public synchronized void disable() {
if (!isEnabled()) {
return;
}
//线程停止
testTask.terminate();
testTask = null;
super.disable();
}
@Override
public void sendCommand(MovementCommand cmd)
throws IllegalArgumentException {
requireNonNull(cmd, "cmd");
}
@Override
public ExplainedBoolean canProcess(List operations) {
requireNonNull(operations, "operations");
final boolean canProcess = isEnabled();
final String reason = canProcess ? "" : "adapter not enabled";
return new ExplainedBoolean(canProcess, reason);
}
@Override
public void processMessage(Object message) {
}
@Override
protected void connectVehicle() {
}
@Override
protected void disconnectVehicle() {
}
@Override
protected boolean isVehicleConnected() {
return true;
}
/**
* 内部类,用于处理运行步骤
*/
private class TestTask
extends CyclicTask {
private TestTask() {
super(0);
}
//线程执行
@Override
protected void runActualTask() {
try {
//获取状态 位置 速度 方向等
String str = socketUtils.send("{\"cmd\":\"Read\",\"pathFrom\":\"\",\"pathTo\":\"\"}");
if (str == null) {
Thread.sleep(1000);
return;
}
if (!str.contains(";")) {
Thread.sleep(1000);
return;
}
String currentPoint = str.split(";")[0];
String currentStatus = str.split(";")[1];
getProcessModel().setVehiclePosition(currentPoint);
if (currentStatus.equals("free")) {
getProcessModel().setVehicleState(Vehicle.State.IDLE);
}
else if (currentStatus.equals("executing")) {
getProcessModel().setVehicleState(Vehicle.State.EXECUTING);
}
final MovementCommand curCommand;
synchronized (TestCommAdapter.this) {
curCommand = getSentQueue().peek();
}
if (curCommand == null) {
Thread.sleep(1000);
return;
}
final Step curStep = curCommand.getStep();
simulateMovement(curStep);
if (!curCommand.isWithoutOperation()) {
simulateOperation(curCommand.getOperation());
}
if (isTerminated()) {
Thread.sleep(1000);
return;
}
if (getSentQueue().size() <= 1 && getCommandQueue().isEmpty()) {
getProcessModel().setVehicleState(Vehicle.State.IDLE);
}
synchronized (TestCommAdapter.this) {
MovementCommand sentCmd = getSentQueue().poll();
if (sentCmd != null && sentCmd.equals(curCommand)) {
getProcessModel().commandExecuted(curCommand);
TestCommAdapter.this.notify();
}
}
Thread.sleep(1000);
}
catch (Exception ex) {
}
}
private void simulateMovement(Step step) throws Exception{
if (step.getPath() == null) {
return;
}
Orientation orientation = step.getVehicleOrientation();
long pathLength = step.getPath().getLength();
int maxVelocity;
switch (orientation) {
case BACKWARD:
maxVelocity = step.getPath().getMaxReverseVelocity();
break;
default:
maxVelocity = step.getPath().getMaxVelocity();
break;
}
String pointName = step.getDestinationPoint().getName();
getProcessModel().setVehicleState(Vehicle.State.EXECUTING);
String currentPoint = "";
String currentStatus = "";
boolean flag = false;
while (!flag) {
String str = socketUtils.send("{\"cmd\":\"Path\",\"pathFrom\":\"" + currentPoint + "\",\"pathTo\":\"" + pointName + "\"}");
if (str.equals("OK")) {
flag = true;
}
Thread.sleep(1000);
}
while (!currentPoint.equals(pointName) && !isTerminated()) {
String str = socketUtils.send("{\"cmd\":\"Read\",\"pathFrom\":\"\",\"pathTo\":\"\"}");
if (str == null) {
Thread.sleep(1000);
continue;
}
if (!str.contains(";")) {
Thread.sleep(1000);
continue;
}
currentPoint = str.split(";")[0];
currentStatus = str.split(";")[1];
getProcessModel().setVehiclePosition(currentPoint);
if (currentStatus.equals("free")) {
getProcessModel().setVehicleState(Vehicle.State.IDLE);
}
else if (currentStatus.equals("executing")) {
getProcessModel().setVehicleState(Vehicle.State.EXECUTING);
}
}
}
private void simulateOperation(String operation) throws Exception{
requireNonNull(operation, "operation");
if (isTerminated()) {
return;
}
// getProcessModel().setVehicleState(Vehicle.State.EXECUTING);
// if (operation.equals(getProcessModel().getLoadOperation())) {
// //getProcessModel().setVehicleLoadHandlingDevices(Arrays.asList(new LoadHandlingDevice(LHD_NAME, true)));
// }
// else if (operation.equals(getProcessModel().getUnloadOperation())) {
// //getProcessModel().setVehicleLoadHandlingDevices(Arrays.asList(new LoadHandlingDevice(LHD_NAME, false)));
// }
}
}
}
附件:AGV模拟工具
链接:https://pan.baidu.com/s/1VgupmQKX0sQvIdzQQ63Q8Q
提取码:n2e8