sige 1 year ago
parent
commit
b7bd4dd841
  1. BIN
      app.db
  2. 6
      src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java
  3. 2
      src/main/java/com/iflytop/digester/underframework/UfApplication.java
  4. 6
      src/main/java/com/iflytop/digester/underframework/connection/UfConnection.java
  5. 2
      src/main/java/com/iflytop/digester/underframework/connection/UfConnectionBase.java
  6. 27
      src/main/java/com/iflytop/digester/underframework/connection/UfConnectionManager.java
  7. 172
      src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java
  8. 88
      src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java
  9. 6
      src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbActuatorCmd.java
  10. 11
      src/main/java/com/iflytop/digester/underframework/util/UfClassHelper.java
  11. 11
      src/main/java/com/iflytop/digester/underframework/web/api/TsApiActuatorCommand.java
  12. 6
      src/main/resources/application.yml
  13. 2
      web

BIN
app.db

6
src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java

@ -1,7 +1,9 @@
package com.iflytop.digester.underframework;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
public class UfActuatorCmdExecutor {
public static void execute(UfMdbActuatorCmd cmd) {
// @TODO : 这里要判断使用那种通讯方式进行执行 所以待定一下
// execute cmd
public static String execute(UfMdbActuatorCmd cmd) {
var con = UfApplication.getApp().connections.get(cmd.connectionKey);
return con.execute(cmd);
}
}

2
src/main/java/com/iflytop/digester/underframework/UfApplication.java

@ -22,7 +22,7 @@ implements ApplicationContextAware, WebMvcConfigurer {
private static UfApplication app = null;
@Resource
private UfConnectionManager connections;
public UfConnectionManager connections;
@Resource
private Environment env;

6
src/main/java/com/iflytop/digester/underframework/connection/UfConnection.java

@ -1,12 +1,14 @@
package com.iflytop.digester.underframework.connection;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
import java.util.Map;
public interface UfConnection {
/**
* execute command and return result
* @param command - command text, like "ls -l"
* @param command - command mdele
* @return result text
*/
String execute( String command );
String execute( UfMdbActuatorCmd command );
/**
* connect to target

2
src/main/java/com/iflytop/digester/underframework/connection/UfConnectionBase.java

@ -8,6 +8,8 @@ abstract public class UfConnectionBase implements UfConnection {
public static final Logger LOG = LoggerFactory.getLogger("UfConnection");
// name
public String name;
// key
public String key;
@Override
public void setProperties(Map<String, Object> properties) {

27
src/main/java/com/iflytop/digester/underframework/connection/UfConnectionManager.java

@ -16,19 +16,24 @@ public class UfConnectionManager {
@PostConstruct
public void init() {
this.connections = new HashMap<>();
// for ( var option : this.connectionOptionList.connections ) {
// String name = (String)option.get("name");
// String type = (String)option.get("type");
// String className = this.getClass().getPackageName() + ".Uf" + type;
// UfConnection con = (UfConnection)UfClassHelper.newInstance(className);
// con.setProperties(option);
// con.connect();
// this.connections.put(name, con);
// }
for ( var option : this.connectionOptionList.connections ) {
String key = (String)option.get("key");
String type = (String)option.get("type");
String className = this.getClass().getPackageName() + ".Uf" + type;
UfConnection con = (UfConnection)UfClassHelper.newInstance(className);
con.setProperties(option);
con.connect();
this.connections.put(key, con);
}
}
// get connection by name
public UfConnection get(String name) {
return this.connections.get(name);
public UfConnection get(String key) {
return this.connections.get(key);
}
// get all connections
public Map<String, UfConnection> getConnections() {
return this.connections;
}
}

172
src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java

@ -0,0 +1,172 @@
package com.iflytop.digester.underframework.connection;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UfModbusRTUOverTCP extends UfConnectionBase {
// connection
private Socket socket;
// writer
private DataOutputStream writer;
// reader
private InputStream reader;
// busy
private Boolean isBusy = false;
// connections
private static final Map<String, UfModbusRTUOverTCP> connections = new HashMap<>();
// get connection
public static UfModbusRTUOverTCP getConnection(String host, Integer port) {
String key = host + ":" + port;
if ( connections.containsKey(key) ) {
return connections.get(key);
}
UfModbusRTUOverTCP con = new UfModbusRTUOverTCP();
con.connect(host, port);
connections.put(key, con);
return con;
}
@Override
public String execute(UfMdbActuatorCmd command) {
return "";
}
@Override
public void connect() {
}
// constructor
public UfModbusRTUOverTCP() {
this.isBusy = false;
}
// connect
public void connect( String host, Integer port ) {
try {
this.socket = new Socket(host, port);
this.writer = new DataOutputStream(this.socket.getOutputStream());
this.reader = this.socket.getInputStream();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// write holding register
public void writeHoldingRegister( Integer slaveId, Integer address, Integer value ) {
byte[] cmd = new byte[8];
cmd[0] = (byte)(slaveId & 0xFF); // slave id
cmd[1] = 0x06; // function code
cmd[2] = (byte)(address >> 8); // address high
cmd[3] = (byte)(address & 0xFF); // address low
cmd[4] = (byte)(value >> 8); // value high
cmd[5] = (byte)(value & 0xFF); // value low
this.call(cmd);
}
// read holding register
public Integer readHoldingRegister( Integer slaveId, Integer address ) {
byte[] cmd = new byte[8];
cmd[0] = (byte)(slaveId & 0xFF); // slave id
cmd[1] = 0x03; // function code
cmd[2] = (byte)(address >> 8); // address high
cmd[3] = (byte)(address & 0xFF); // address low
cmd[4] = 0x00; // value high
cmd[5] = 0x01; // value low
var response = this.call(cmd);
if (response.isEmpty()) {
throw new RuntimeException("Modbus RTU over TCP: no response");
}
return response.get(3) << 8 | response.get(4);
}
// call
private List<Integer> call(byte[] cmd) {
if ( this.isBusy ) {
synchronized ( this ) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
this.isBusy = true;
this.writeCmd(cmd);
List<Integer> response = this.readCmdResponse();
this.isBusy = false;
synchronized ( this ) {
this.notify();
}
return response;
}
// write command
private void writeCmd(byte[] cmd) {
var size = cmd.length;
var crc = this.calculateCRC(cmd, size - 2);
cmd[size-2] = (byte)(crc & 0xFF);
cmd[size-1] = (byte)(crc >> 8);
try {
this.writer.write(cmd);
this.writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// read command response
private List<Integer> readCmdResponse ( ) {
try {
List<Integer> result = new ArrayList<>();
while ( 0 < this.reader.available() ) {
result.add(this.reader.read());
}
return result;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 计算输入数据的CRC16 (Modbus)
* @param data 要计算CRC的字节数组
* @param length 数组中需要计算的长度
* @return 计算出的CRC16值
*/
public int calculateCRC(byte[] data, int length) {
int crc = 0xFFFF; // CRC的初始值
for (int pos = 0; pos < length; pos++) {
crc ^= data[pos] & 0xFF; // 将数据与CRC寄存器异或
for (int i = 8; i != 0; i--) { // 循环处理每个位
if ((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001; // 0xA001是预设的多项式
} else {
crc >>= 1;
}
}
}
// 高低位不交换直接返回
return crc & 0xFFFF;
}
}

88
src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java

@ -2,6 +2,7 @@ package com.iflytop.digester.underframework.connection;
import com.iflytop.digester.underframework.dao.model.TsMdbActuator;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
import com.iflytop.digester.underframework.util.TsByteBuffer;
import com.iflytop.digester.underframework.util.UfClassHelper;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
@ -57,42 +58,28 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
}
@Override
public String execute(String command) {
return null;
synchronized public String execute(UfMdbActuatorCmd command) {
var parts = command.cmdKey.split("_");
for (int i = 0; i < parts.length; i++) {
parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1);
}
String methodName = "cmd" + String.join("", parts);
String returnValue = "";
// execute command
synchronized public void execute( UfMdbActuatorCmd actuatorCmd ) {
this.executeDeviceCommand(actuatorCmd);
if ( 1 == actuatorCmd.waitForFinish ) {
this.waitForActuatorFinish(actuatorCmd);
}
}
// wait for actuator finish
private void waitForActuatorFinish(UfMdbActuatorCmd actuatorCmd) {
do {
var waitCmd = new UfMdbActuatorCmd();
waitCmd.actuatorId = actuatorCmd.actuatorId;
waitCmd.cmdId = "0104";
waitCmd.cmdKey = "module_get_status";
waitCmd.parameters = "";
this.executeDeviceCommand(waitCmd);
int status = this.response.getInt(8);
if ( 0 == status ) {
break;
try {
returnValue = (String) UfClassHelper.invokeMethod(this, methodName, List.of(command));
} catch (NoSuchMethodException e) {
returnValue = this.executeDeviceCommand(command);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
if ( 1 == command.waitForFinish ) {
this.waitForActuatorFinish(command);
}
} while ( true );
return returnValue;
}
// execute device command
private void executeDeviceCommand( UfMdbActuatorCmd actuatorCmd ) {
private String executeDeviceCommand( UfMdbActuatorCmd actuatorCmd ) {
this.response = null;
this.responseError = null;
this.sendCommandRequest(actuatorCmd);
@ -108,6 +95,21 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
if ( null != this.responseError ) {
throw new RuntimeException(this.responseError);
}
var responseLength = this.response.capacity();
if ( 8 == responseLength ) {
return "";
} else if ( 12 == responseLength ) {
int value = this.response.getInt(8);
return String.valueOf(value);
} else if ( 12 < responseLength ) {
byte[] bytes = new byte[this.response.limit() - 8];
this.response.position(8);
this.response.get(bytes);
return java.util.Base64.getEncoder().encodeToString(bytes);
} else {
throw new RuntimeException("unknown response length: " + this.response.limit());
}
}
// send command request
@ -139,7 +141,7 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
}
TsMdbActuator actuator = TsMdbActuator.findOne(TsMdbActuator.class, actuatorCmd.actuatorId);
int moduleId = 0; //actuator.aid;
int moduleId = Integer.parseInt(actuatorCmd.cmdFlags);
int cmdId = Integer.parseInt(actuatorCmd.cmdId, 16);
int subCmdId = cmdId & 0xFF;
@ -199,14 +201,32 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
// handle on data timeout for binary mode ack message
private void handleOnTextAckMessage(ByteBuffer message) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.response = message;
synchronized (this.callLock) {
this.callLock.notifyAll();
}
}
// wait for actuator finish
private void waitForActuatorFinish(UfMdbActuatorCmd actuatorCmd) {
do {
var waitCmd = new UfMdbActuatorCmd();
waitCmd.actuatorId = actuatorCmd.actuatorId;
waitCmd.cmdId = "0104";
waitCmd.cmdKey = "module_get_status";
waitCmd.cmdFlags = actuatorCmd.cmdFlags;
waitCmd.parameters = "";
String statusText = this.executeDeviceCommand(waitCmd);
int status = Integer.parseInt(statusText);
if ( 0 == status ) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} while ( true );
}
}

6
src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbActuatorCmd.java

@ -6,6 +6,9 @@ public class UfMdbActuatorCmd extends UfActiveRecord {
public String actuatorId;
@UfActiveRecordField
public String connectionKey;
@UfActiveRecordField
public String name;
@UfActiveRecordField
@ -15,6 +18,9 @@ public class UfMdbActuatorCmd extends UfActiveRecord {
public String cmdKey;
@UfActiveRecordField
public String cmdFlags;
@UfActiveRecordField
public String fixedParameters;
@UfActiveRecordField

11
src/main/java/com/iflytop/digester/underframework/util/UfClassHelper.java

@ -20,6 +20,7 @@ public class UfClassHelper {
}
}
// new instance
public static Object newInstance(String className) {
try {
return Class.forName(className).getDeclaredConstructor().newInstance();
@ -28,6 +29,16 @@ public class UfClassHelper {
}
}
// check method exist
public static Boolean isMethodExist(Object obj, String methodName) {
try {
obj.getClass().getMethod(methodName);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
// invoke method
public static Object invokeMethod(Object obj, String methodName, List<Object> args) throws NoSuchMethodException {
try {

11
src/main/java/com/iflytop/digester/underframework/web/api/TsApiActuatorCommand.java

@ -1,4 +1,6 @@
package com.iflytop.digester.underframework.web.api;
import com.iflytop.digester.underframework.UfActuatorCmdExecutor;
import com.iflytop.digester.underframework.UfApplication;
import com.iflytop.digester.underframework.dao.record.UfActiveRecord;
import com.iflytop.digester.underframework.dao.record.UfActiveRecordCriteria;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
@ -40,10 +42,17 @@ public class TsApiActuatorCommand extends UfApiControllerBase {
@ResponseBody
public UfApiResponse execute(@RequestBody UfMdbActuatorCmd actuatorCmd ) {
try {
// UfApplication.getApp().commandExecutor.execute(actuatorCmd);
UfActuatorCmdExecutor.execute(actuatorCmd);
} catch ( Exception e ) {
return error(e.getMessage());
}
return success();
}
@PostMapping("/api/actuator-cmd/connection-list")
@ResponseBody
public UfApiResponse connectionList() {
var connections = UfApplication.getApp().connections;
return success(connections.getConnections());
}
}

6
src/main/resources/application.yml

@ -12,8 +12,14 @@ device:
snippet-execute-log-enable : false
connections:
- name : zcancmder
key : zcancmder
type : ZcancmderWebsocket
uri: ws://192.168.8.10:19005
- name : modbus
key : modbus
type : ModbusRTUOverTCP
host: 182.168.8.10
port: 502
mqtt-broker:
uri: tcp://broker.emqx.io:1883

2
web

@ -1 +1 @@
Subproject commit 6bacffe10495394dfa5cf3a793d2aa7a60ef164c
Subproject commit 54eb4327c3a74e6aae926d4752b18439f62091f0
Loading…
Cancel
Save