26 changed files with 3113 additions and 1032 deletions
@ -0,0 +1 @@ |
|||||
|
fivewheel |
||||
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="VcsDirectoryMappings"> |
||||
|
<mapping directory="" vcs="Git" /> |
||||
|
</component> |
||||
|
</project> |
||||
@ -1,23 +1,28 @@ |
|||||
syntax = "proto3"; |
syntax = "proto3"; |
||||
option java_multiple_files = false; |
option java_multiple_files = false; |
||||
option java_package = "com.example.removemarineanimals.models"; |
option java_package = "com.example.fivewheel.models"; |
||||
|
|
||||
message IV_struct_define{ |
message IV_struct_define{ |
||||
|
|
||||
// 五轮项目 |
// 五轮项目 |
||||
int32 Robot_Move_AutoSpeed= 1; |
// 五轮项目 |
||||
int32 Robot_Move_ManualSpeed= 2; |
int32 Robot_Move_AutoSpeed= 1; |
||||
int32 Robot_CurrentPosition= 3; |
int32 Robot_Move_ManualSpeed= 2; |
||||
int32 Robot_AngleRoll= 4; |
int32 Robot_CurrentPosition= 3; |
||||
int32 Robot_Error= 5; |
int32 Robot_AngleRoll= 4; |
||||
int32 Robot_DynamometerValue= 6; |
int32 Robot_Error= 5; |
||||
int32 Robot_ForceValue= 7; |
int32 Robot_DynamometerValue= 6; |
||||
int32 Robot_CurrentState= 8; |
int32 Robot_ForceValue= 7; |
||||
int32 Robot_Current_Left= 9; |
int32 Robot_CurrentState= 8; |
||||
int32 Robot_Current_Right= 10; |
int32 Robot_Current_Left= 9; |
||||
int32 Robot_Error_Left = 11; |
int32 Robot_Current_Right= 10; |
||||
int32 Robot_Error_Right = 12; |
int32 Robot_Error_Left= 11; |
||||
int32 Robot_Compensation_Left = 13; |
int32 Robot_Error_Right= 12; |
||||
int32 Robot_Compensation_Right = 14; |
int32 Robot_Compensation_Left= 13; |
||||
int32 Robot_RESET = 15; |
int32 Robot_Compensation_Right= 14; |
||||
|
int32 Robot_RESET = 15; |
||||
|
int64 TimeStamp=16; |
||||
|
int32 RobotRestart=17; |
||||
|
int32 RobotAngle=18; |
||||
|
int32 SystemError=19; |
||||
}; |
}; |
||||
|
|||||
@ -1,12 +1,14 @@ |
|||||
syntax = "proto3"; |
syntax = "proto3"; |
||||
|
|
||||
option java_multiple_files = false; |
option java_multiple_files = false; |
||||
option java_package = "com.example.removemarineanimals.models"; |
option java_package = "com.example.fivewheel.models"; |
||||
message PV_struct_define{ |
message PV_struct_define{ |
||||
|
|
||||
int32 Robot_ChgLength= 1; |
int32 Robot_ChgLength= 1; |
||||
double Robot_AutoSpeedBase=2; |
double Robot_AutoSpeedBase=2; |
||||
double Robot_ManualSpeedBase=3; |
double Robot_ManualSpeedBase=3; |
||||
int32 Robot_LaneChange_Direction = 4; |
int32 Robot_LaneChange_Direction = 4; |
||||
int32 Robot_Force = 5; |
int32 Robot_Force = 5; |
||||
|
int64 TimeStamp=6;//上位机发送下来的时间戳 |
||||
|
int32 RobotRestartAccepted=7;//上位机接收到单片机发送的Restart信号 |
||||
}; |
}; |
||||
|
|||||
@ -1,335 +0,0 @@ |
|||||
package com.example.removemarineanimals; |
|
||||
|
|
||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||
import androidx.core.content.ContextCompat; |
|
||||
import androidx.databinding.DataBindingUtil; |
|
||||
import androidx.lifecycle.ViewModelProvider; |
|
||||
|
|
||||
import android.app.PendingIntent; |
|
||||
import android.content.BroadcastReceiver; |
|
||||
import android.content.Context; |
|
||||
import android.content.Intent; |
|
||||
import android.content.IntentFilter; |
|
||||
import android.hardware.usb.UsbDevice; |
|
||||
import android.hardware.usb.UsbDeviceConnection; |
|
||||
import android.hardware.usb.UsbManager; |
|
||||
import android.os.Build; |
|
||||
import android.os.Bundle; |
|
||||
|
|
||||
import com.example.removemarineanimals.databinding.ActivityMainBinding; |
|
||||
import com.example.removemarineanimals.services.CustomProber; |
|
||||
//import com.example.removemarineanimals.services.USBSerialPortHelper; |
|
||||
import com.example.removemarineanimals.services.VideoHelper; |
|
||||
import com.example.removemarineanimals.viewmodels.MainViewModel; |
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver; |
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort; |
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber; |
|
||||
import com.hoho.android.usbserial.util.SerialInputOutputManager; |
|
||||
|
|
||||
import android.os.Bundle; |
|
||||
import android.os.CountDownTimer; |
|
||||
import android.os.Handler; |
|
||||
import android.os.Looper; |
|
||||
|
|
||||
import java.io.IOException; |
|
||||
import java.util.ArrayList; |
|
||||
import java.util.Iterator; |
|
||||
import java.util.List; |
|
||||
|
|
||||
import cn.nodemedia.NodePlayer; |
|
||||
|
|
||||
|
|
||||
public class MainActivity extends AppCompatActivity implements SerialInputOutputManager.Listener { |
|
||||
|
|
||||
|
|
||||
private enum UsbPermission {Unknown, Requested, Granted, Denied} |
|
||||
|
|
||||
private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB"; |
|
||||
private int deviceId = 60000; |
|
||||
private int deviceId_test = 60000; |
|
||||
private int portNum; |
|
||||
private static final int WRITE_WAIT_MILLIS = 500; |
|
||||
private static final int READ_WAIT_MILLIS = 1000; |
|
||||
private static String PortNameContians = "SILICON";/**/ |
|
||||
private int baudRate = 57600; |
|
||||
private boolean withIoManager = true; |
|
||||
|
|
||||
private BroadcastReceiver broadcastReceiver; |
|
||||
private Handler mainLooper; |
|
||||
|
|
||||
|
|
||||
private SerialInputOutputManager usbIoManager; |
|
||||
private UsbSerialPort usbSerialPort; |
|
||||
private UsbPermission usbPermission = UsbPermission.Unknown; |
|
||||
private boolean connected = false; |
|
||||
|
|
||||
|
|
||||
public void GetControlsReferences() { |
|
||||
broadcastReceiver = new BroadcastReceiver() { |
|
||||
@Override |
|
||||
public void onReceive(Context context, Intent intent) { |
|
||||
if (INTENT_ACTION_GRANT_USB.equals(intent.getAction())) { |
|
||||
usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) |
|
||||
? UsbPermission.Granted : UsbPermission.Denied; |
|
||||
connect(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
}; |
|
||||
mainLooper = new Handler(Looper.getMainLooper()); |
|
||||
|
|
||||
|
|
||||
_receiveBufferlist = new ArrayList<Byte>(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public static ActivityMainBinding mainBinding;//通过Binding可以获取界面数据 |
|
||||
// public USBSerialPortHelper serialPortHelper; |
|
||||
|
|
||||
@Override |
|
||||
protected void onCreate(Bundle savedInstanceState) { |
|
||||
super.onCreate(savedInstanceState); |
|
||||
// setContentView(R.layout.activity_main); |
|
||||
mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); |
|
||||
MainViewModel vm = new ViewModelProvider(this).get(MainViewModel.class); |
|
||||
MainViewModel.mainBinding = mainBinding; |
|
||||
// vm.mainBinding=mainBinding; |
|
||||
mainBinding.setVm(vm); |
|
||||
|
|
||||
|
|
||||
//nodePlayer0 =new NodePlayer(this); |
|
||||
//nodePlayer1 =new NodePlayer(this); |
|
||||
|
|
||||
VideoHelper.nodePlayerView0 = mainBinding.nodePlayerView0; |
|
||||
VideoHelper.nodePlayerView1 = mainBinding.nodePlayerView1; |
|
||||
|
|
||||
VideoHelper.nodePlayer0 = new NodePlayer(this); |
|
||||
VideoHelper.nodePlayer1 = new NodePlayer(this); |
|
||||
|
|
||||
|
|
||||
VideoHelper.StatPlayVideo(); |
|
||||
|
|
||||
// |
|
||||
// serialPortHelper=new USBSerialPortHelper(); |
|
||||
// serialPortHelper.MainActivity=this; |
|
||||
// serialPortHelper.intialize(); |
|
||||
|
|
||||
|
|
||||
GetControlsReferences(); |
|
||||
connect(); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected void onStart() { |
|
||||
super.onStart(); |
|
||||
ContextCompat.registerReceiver(this, broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB), ContextCompat.RECEIVER_NOT_EXPORTED); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onStop() { |
|
||||
this.unregisterReceiver(broadcastReceiver); |
|
||||
super.onStop(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onResume() { |
|
||||
super.onResume(); |
|
||||
if (!connected && (usbPermission == UsbPermission.Unknown || usbPermission == UsbPermission.Granted)) { |
|
||||
//mainLooper.post(this::connect); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onPause() { |
|
||||
if (connected) { |
|
||||
status("串口断开"); |
|
||||
// _serialPortSwitch.setChecked(false); |
|
||||
disconnect(); |
|
||||
} |
|
||||
super.onPause(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onNewData(byte[] data) { |
|
||||
mainLooper.post(() -> |
|
||||
{ |
|
||||
receive(data); |
|
||||
// receive data |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onRunError(Exception e) { |
|
||||
mainLooper.post(() -> |
|
||||
{ |
|
||||
status("connection lost: " + e.getMessage()); |
|
||||
disconnect(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void connect() { |
|
||||
|
|
||||
UsbDevice device = null; |
|
||||
UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE); |
|
||||
for (UsbDevice v : usbManager.getDeviceList().values()) { |
|
||||
status(v.getManufacturerName().toUpperCase()); |
|
||||
if (v.getManufacturerName().toUpperCase().contains(PortNameContians)) { |
|
||||
device = v; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (device == null) { |
|
||||
// _serialPortSwitch.setChecked(false); |
|
||||
|
|
||||
status("找不到设备"); |
|
||||
return; |
|
||||
} |
|
||||
UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device); |
|
||||
if (driver == null) { |
|
||||
driver = CustomProber.getCustomProber().probeDevice(device); |
|
||||
} |
|
||||
if (driver == null) { |
|
||||
// _serialPortSwitch.setChecked(false); |
|
||||
status("无驱动"); |
|
||||
return; |
|
||||
} |
|
||||
if (driver.getPorts().size() < portNum) //就是0 cp2102 或者同一个驱动,第一个 |
|
||||
{ |
|
||||
status("connection failed: not enough ports at device"); |
|
||||
status("找不到设备"); |
|
||||
return; |
|
||||
} |
|
||||
usbSerialPort = driver.getPorts().get(portNum); |
|
||||
|
|
||||
UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice()); |
|
||||
if (usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) { |
|
||||
usbPermission = UsbPermission.Requested; |
|
||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_MUTABLE : 0; |
|
||||
Intent intent = new Intent(INTENT_ACTION_GRANT_USB); |
|
||||
intent.setPackage(this.getPackageName()); |
|
||||
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(this, 0, intent, flags); |
|
||||
usbManager.requestPermission(driver.getDevice(), usbPermissionIntent); |
|
||||
return; |
|
||||
} |
|
||||
if (usbConnection == null) { |
|
||||
if (!usbManager.hasPermission(driver.getDevice())) { |
|
||||
status("connection failed: permission denied"); |
|
||||
} else { |
|
||||
status("connection failed: open failed"); |
|
||||
} |
|
||||
|
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
usbSerialPort.open(usbConnection); |
|
||||
try { |
|
||||
usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); |
|
||||
} catch (UnsupportedOperationException e) { |
|
||||
status("unsupport setparameters"); |
|
||||
} |
|
||||
if (withIoManager) { |
|
||||
usbIoManager = new SerialInputOutputManager(usbSerialPort, this); |
|
||||
usbIoManager.setReadBufferSize(40960); |
|
||||
usbIoManager.setReadTimeout(READ_WAIT_MILLIS); |
|
||||
usbIoManager.start(); |
|
||||
} |
|
||||
//status("connected"); |
|
||||
connected = true; |
|
||||
// _serialPortSwitch.setChecked(true); |
|
||||
//switch set true |
|
||||
|
|
||||
} catch (Exception e) { |
|
||||
status("connection failed: " + e.getMessage()); |
|
||||
disconnect(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void disconnect() { |
|
||||
connected = false; |
|
||||
|
|
||||
if (usbIoManager != null) { |
|
||||
usbIoManager.setListener(null); |
|
||||
usbIoManager.stop(); |
|
||||
} |
|
||||
usbIoManager = null; |
|
||||
try { |
|
||||
usbSerialPort.close(); |
|
||||
} catch (IOException ignored) |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
usbSerialPort = null; |
|
||||
} |
|
||||
|
|
||||
List<Byte> _receiveBufferlist; |
|
||||
|
|
||||
private byte[] listTobyte(List<Byte> list) { |
|
||||
if (list == null || list.size() < 0) |
|
||||
return null; |
|
||||
byte[] bytes = new byte[list.size()]; |
|
||||
int i = 0; |
|
||||
Iterator<Byte> iterator = list.iterator(); |
|
||||
while (iterator.hasNext()) { |
|
||||
bytes[i] = iterator.next(); |
|
||||
i++; |
|
||||
} |
|
||||
return bytes; |
|
||||
} |
|
||||
|
|
||||
public int Counter = 1000; |
|
||||
boolean StartCountDown = false; |
|
||||
|
|
||||
private void receive(byte[] data) { |
|
||||
|
|
||||
for (int i = 0; i < data.length; i++) { |
|
||||
_receiveBufferlist.add(data[i]); |
|
||||
} |
|
||||
|
|
||||
//decodeRceive(data); |
|
||||
if (StartCountDown == false) { |
|
||||
StartCountDown = true; |
|
||||
new CountDownTimer(500, 500) { |
|
||||
public void onTick(long millisUntilFinished) { |
|
||||
// Used for formatting digit to be in 2 digits only |
|
||||
|
|
||||
} |
|
||||
|
|
||||
// When the task is over it will print 00:00:00 there |
|
||||
public void onFinish() { |
|
||||
decodeRceive(listTobyte(_receiveBufferlist)); |
|
||||
_receiveBufferlist.clear(); |
|
||||
StartCountDown = false; |
|
||||
} |
|
||||
}.start(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
private void decodeRceive(byte[] data) { |
|
||||
try { |
|
||||
|
|
||||
} catch ( |
|
||||
Exception e) { |
|
||||
//spn.append("exception:{e} "); |
|
||||
} |
|
||||
} |
|
||||
void status(String str) |
|
||||
{ |
|
||||
mainBinding.message.setText(str); |
|
||||
// SpannableStringBuilder spn = new SpannableStringBuilder(str + '\r' + '\n'); |
|
||||
// |
|
||||
// // spn.append(getTime()); |
|
||||
// |
|
||||
// spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorAccent)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
|
||||
// receiveText.append(spn); |
|
||||
// scrollView.fullScroll(ScrollView.FOCUS_DOWN); |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -0,0 +1,6 @@ |
|||||
|
package com.example.fivewheel.services; |
||||
|
|
||||
|
|
||||
|
public enum CommunicationMethond { |
||||
|
Wireless, Wired |
||||
|
} |
||||
@ -0,0 +1,154 @@ |
|||||
|
package com.example.fivewheel.services; |
||||
|
|
||||
|
import com.example.fivewheel.models.BspIV; |
||||
|
import com.example.fivewheel.models.BspPV; |
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
|
||||
|
public class DataExchangeHelper { |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
public static int[] decodedCH = new int[17]; |
||||
|
static double slope = 1000.0 / (1950 - 1500); |
||||
|
|
||||
|
public static int[] getdecodedCH(byte[] receivedData) { |
||||
|
for (int i = 0; i < 16; i++) { |
||||
|
decodedCH[i] = ((receivedData[8 + i * 2] & 0xFF) | (receivedData[9 + i * 2] & 0xFF) << 8); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < 16; i++) { |
||||
|
decodedCH[i] = (int) Math.round(slope * (decodedCH[i] - 1500)); |
||||
|
} |
||||
|
return decodedCH; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static byte[] getSendPVBytes(BspPV.PV_struct_define _toSendPV) { |
||||
|
byte[] byteArray = _toSendPV.toByteArray(); |
||||
|
byte[] sendbyteArray = new byte[byteArray.length + 4]; |
||||
|
byte[] sendbyteArray3 = new byte[byteArray.length + 6]; |
||||
|
if (byteArray.length != 0) { |
||||
|
System.arraycopy(byteArray, 0, sendbyteArray, 4, byteArray.length); |
||||
|
} |
||||
|
sendbyteArray[0] = (byte) 0x55; |
||||
|
sendbyteArray[1] = (byte) 0x55; |
||||
|
sendbyteArray[2] = (byte) 0x01; |
||||
|
sendbyteArray[3] = (byte) 0x01; |
||||
|
byte[] byteArray2 = ModbusCRC.calculateCRC(sendbyteArray); |
||||
|
System.arraycopy(sendbyteArray, 0, sendbyteArray3, 0, sendbyteArray.length); |
||||
|
System.arraycopy(byteArray2, 0, sendbyteArray3, sendbyteArray3.length - 2, 2); |
||||
|
return sendbyteArray3; |
||||
|
} |
||||
|
|
||||
|
public static BspIV.IV_struct_define getIVByBytes(byte[] data) { |
||||
|
byte[] crcbytes = new byte[data.length - 2]; |
||||
|
System.arraycopy(data, 0, crcbytes, 0, data.length - 2); |
||||
|
byte[] crc = ModbusCRC.calculateCRC(crcbytes); |
||||
|
// status(bytesToHex(data));
|
||||
|
if (data[data.length - 2] == (byte) (crc[1] & 0xff) && data[data.length - 1] == (byte) (crc[0] & 0xff)) { |
||||
|
if ((data[0] == 0x55) && (data[1] == 0x55)) { |
||||
|
byte[] bytes = new byte[data.length - 4]; |
||||
|
System.arraycopy(data, 2, bytes, 0, data.length - 4); |
||||
|
try { |
||||
|
BspIV.IV_struct_define _toReceiveIV = BspIV.IV_struct_define.parseFrom(bytes); |
||||
|
return _toReceiveIV; |
||||
|
} catch (InvalidProtocolBufferException ex) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static BspIV.IV_struct_define getIVByModbus() { |
||||
|
|
||||
|
try { |
||||
|
//定义100 是IV的数据开头数组个数 建立多少个字节数组
|
||||
|
byte[] bytes = new byte[ModbusRtuSlaveService.holdingRegisters[100]]; |
||||
|
int modifyHoldingRegisterNum = (bytes.length + 1) / 2; |
||||
|
for (int i = 0; i < modifyHoldingRegisterNum; i++) { |
||||
|
bytes[2 * i] = (byte) (ModbusRtuSlaveService.holdingRegisters[101 + i] >> 8); |
||||
|
if (2 * i + 1 > bytes.length - 1) { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
bytes[2 * i + 1] = (byte) (ModbusRtuSlaveService.holdingRegisters[101 + i] & 0xff); |
||||
|
} |
||||
|
|
||||
|
BspIV.IV_struct_define _toReceiveIV = BspIV.IV_struct_define.parseFrom(bytes); |
||||
|
return _toReceiveIV; |
||||
|
} catch (InvalidProtocolBufferException ex) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//_toSendPV 换算成bytes
|
||||
|
public static void setModbusPVValues(BspPV.PV_struct_define _toSendPV) { |
||||
|
|
||||
|
byte[] byteArray = _toSendPV.toByteArray(); |
||||
|
byte[] bytesToSend; |
||||
|
int modifyHoldingRegisterNum = (byteArray.length + 1) / 2; |
||||
|
|
||||
|
if (byteArray.length != 0) { |
||||
|
if (byteArray.length % 2 != 0) { |
||||
|
bytesToSend = new byte[byteArray.length + 1]; |
||||
|
bytesToSend[bytesToSend.length - 1] = 0; |
||||
|
|
||||
|
} else { |
||||
|
bytesToSend = new byte[byteArray.length]; |
||||
|
} |
||||
|
System.arraycopy(byteArray, 0, bytesToSend, 0, byteArray.length); |
||||
|
|
||||
|
ModbusRtuSlaveService.holdingRegisters[18] = (short) byteArray.length; |
||||
|
//将数据转为小端发送
|
||||
|
for (int i = 0; i < modifyHoldingRegisterNum; i++) { |
||||
|
|
||||
|
|
||||
|
ModbusRtuSlaveService.holdingRegisters[19 + i] = (short) ( |
||||
|
((bytesToSend[2 * i + 1] & 0xFF) << 8) | // 高位字节
|
||||
|
bytesToSend[2 * i] & 0xFF); // 低位字节
|
||||
|
|
||||
|
// (short) ((bytesToSend[2 * i + 1]) << 8 | ((short)bytesToSend[2 * i]));
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 判断字符串是否是数字 |
||||
|
*/ |
||||
|
public static boolean isNumber(String value) { |
||||
|
return isInteger(value) || isDouble(value); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断字符串是否是整数 |
||||
|
*/ |
||||
|
public static boolean isInteger(String value) { |
||||
|
try { |
||||
|
Integer.parseInt(value); |
||||
|
return true; |
||||
|
} catch (NumberFormatException e) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断字符串是否是浮点数 |
||||
|
*/ |
||||
|
public static boolean isDouble(String value) { |
||||
|
try { |
||||
|
Double.parseDouble(value); |
||||
|
if (value.contains(".")) |
||||
|
return true; |
||||
|
return false; |
||||
|
} catch (NumberFormatException e) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
package com.example.fivewheel.services; |
||||
|
|
||||
|
|
||||
|
import com.example.fivewheel.models.BspError; |
||||
|
|
||||
|
public class ErrorDeocdeHelper { |
||||
|
|
||||
|
//private static Map<String, Integer> flagMap = new HashMap<>();
|
||||
|
|
||||
|
// public static void AddMap(String errorName, Integer bitPosition)
|
||||
|
// {
|
||||
|
// flagMap.put(errorName,bitPosition);
|
||||
|
// }
|
||||
|
private static String ErrorResult; |
||||
|
private static String addErrorResult; |
||||
|
public static String ErrorDeocde(int data) |
||||
|
{ |
||||
|
ErrorResult=""; |
||||
|
// 循环遍历 Map
|
||||
|
// for (Map.Entry<String, Integer> entry : flagMap.entrySet()) {
|
||||
|
// String key = entry.getKey();
|
||||
|
// int bitPosiiton = entry.getValue();
|
||||
|
//
|
||||
|
// // 使用左移操作生成要检查的标志位
|
||||
|
// int bitToCheck = 1 << bitPosiiton;
|
||||
|
//
|
||||
|
// // 判断该位置是否有对应的标志位
|
||||
|
// if ((data & bitToCheck) != 0)
|
||||
|
// {
|
||||
|
// ErrorResult+=key+":\t 错误! ";
|
||||
|
// } else
|
||||
|
// {
|
||||
|
//
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
|
||||
|
for (BspError.ComError err : BspError.ComError.values()) { |
||||
|
|
||||
|
|
||||
|
int bitPosiiton = err.ordinal(); |
||||
|
if (err.toString().equals("UNRECOGNIZED")) continue; |
||||
|
if (err.toString().equals("TL720D")) |
||||
|
addErrorResult = " 作业模式已切换至手动"; |
||||
|
else |
||||
|
addErrorResult = " "; |
||||
|
|
||||
|
// 使用左移操作生成要检查的标志位
|
||||
|
int bitToCheck = 1 << bitPosiiton; |
||||
|
|
||||
|
// 判断该位置是否有对应的标志位
|
||||
|
if ((data & bitToCheck) != 0) |
||||
|
{ |
||||
|
|
||||
|
ErrorResult+=err.toString()+addErrorResult+"\t"; |
||||
|
} else |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
return ErrorResult; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,517 @@ |
|||||
|
package com.example.fivewheel.services; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
|
||||
|
public class ModbusRtuSlaveService { |
||||
|
public static void ModbusRtuSlaveServiceIntialize(USBSerialPortHelper serialPortHelper) { |
||||
|
_serialPortHelper = serialPortHelper; |
||||
|
initializeData(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static final String TAG = "ModbusRtuSlave"; |
||||
|
public static final int slaveId = 0x40; |
||||
|
public static Thread readThread; |
||||
|
public final AtomicBoolean shouldRead = new AtomicBoolean(false); |
||||
|
public static USBSerialPortHelper _serialPortHelper; |
||||
|
|
||||
|
// Modbus 数据存储
|
||||
|
public static final boolean[] coils = new boolean[65536]; |
||||
|
public static final boolean[] discreteInputs = new boolean[65536]; |
||||
|
public static final short[] holdingRegisters = new short[65536]; |
||||
|
public static final short[] inputRegisters = new short[65536]; |
||||
|
|
||||
|
// 数据更新监听器
|
||||
|
public DataUpdateListener dataUpdateListener; |
||||
|
|
||||
|
public interface DataUpdateListener { |
||||
|
void onCoilUpdated(int address, boolean value); |
||||
|
|
||||
|
void onRegisterUpdated(int address, short value); |
||||
|
|
||||
|
void onError(String errorMessage); |
||||
|
} |
||||
|
|
||||
|
public static void initializeData() { |
||||
|
// 初始化示例数据
|
||||
|
Arrays.fill(holdingRegisters, (short) 0); |
||||
|
Arrays.fill(inputRegisters, (short) 0); |
||||
|
Arrays.fill(coils, false); |
||||
|
Arrays.fill(discreteInputs, false); |
||||
|
|
||||
|
// 设置一些默认值 可以处理一些默认值 后面
|
||||
|
|
||||
|
|
||||
|
// 温度
|
||||
|
holdingRegisters[1] = 60; // 湿度
|
||||
|
holdingRegisters[2] = 1000; // 压力
|
||||
|
coils[0] = true; // 运行状态
|
||||
|
coils[1] = false; // 报警状态
|
||||
|
|
||||
|
for (short i = 0; i < 200; i++) { |
||||
|
holdingRegisters[i] = i; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
public final byte[] buffer = new byte[256]; |
||||
|
|
||||
|
//处理Modbus数据,这个可以在数据接收后调用 返回调用的功能码
|
||||
|
public static int processModbusRequest(byte[] request, int length) { |
||||
|
if (length < 4) { |
||||
|
Log.w(TAG, "Request too short: " + length + " bytes"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// 提取从站地址
|
||||
|
byte receivedSlaveId = request[0]; |
||||
|
if (receivedSlaveId != slaveId && receivedSlaveId != 0) { |
||||
|
Log.d(TAG, "Request not for this slave. Target: " + receivedSlaveId + ", Our ID: " + slaveId); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
Log.d(TAG, "Processing request for slave: " + receivedSlaveId); |
||||
|
|
||||
|
// 验证CRC
|
||||
|
if (!validateCrc(request, length)) { |
||||
|
Log.w(TAG, "CRC validation failed"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// 处理Modbus请求
|
||||
|
byte[] response = handleModbusFrame(request, length); |
||||
|
if (response != null && response.length > 0) { |
||||
|
try { |
||||
|
sendResponse(response); |
||||
|
return request[1];//返回功能码
|
||||
|
} catch (Exception e) { |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleModbusFrame(byte[] frame, int length) { |
||||
|
if (length < 4) return null; |
||||
|
|
||||
|
byte functionCode = frame[1]; |
||||
|
byte[] response;// null;
|
||||
|
|
||||
|
Log.d(TAG, "Handling function code: 0x" + String.format("%02X", functionCode)); |
||||
|
|
||||
|
try { |
||||
|
switch (functionCode) { |
||||
|
case 0x01: // Read Coils
|
||||
|
response = handleReadCoils(frame, length); |
||||
|
break; |
||||
|
case 0x02: // Read Discrete Inputs
|
||||
|
response = handleReadDiscreteInputs(frame, length); |
||||
|
break; |
||||
|
case 0x03: // Read Holding Registers
|
||||
|
response = handleReadHoldingRegisters(frame, length); |
||||
|
break; |
||||
|
case 0x04: // Read Input Registers
|
||||
|
response = handleReadInputRegisters(frame, length); |
||||
|
break; |
||||
|
case 0x05: // Write Single Coil
|
||||
|
response = handleWriteSingleCoil(frame, length); |
||||
|
break; |
||||
|
case 0x06: // Write Single Register
|
||||
|
response = handleWriteSingleRegister(frame, length); |
||||
|
break; |
||||
|
case 0x0F: // Write Multiple Coils
|
||||
|
response = handleWriteMultipleCoils(frame, length); |
||||
|
break; |
||||
|
case 0x10: // Write Multiple Registers
|
||||
|
response = handleWriteMultipleRegisters(frame, length); |
||||
|
break; |
||||
|
default: |
||||
|
Log.w(TAG, "Unsupported function code: 0x" + String.format("%02X", functionCode)); |
||||
|
response = createExceptionResponse(functionCode, (byte) 0x01); // Illegal function
|
||||
|
break; |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "Error handling Modbus request: " + e.getMessage()); |
||||
|
response = createExceptionResponse(functionCode, (byte) 0x04); // Slave device failure
|
||||
|
} |
||||
|
|
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleWriteMultipleRegisters(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
int byteCount = request[6] & 0xFF; |
||||
|
|
||||
|
Log.d(TAG, "Write Multiple Registers - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
if (quantity < 1 || quantity > 123) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > holdingRegisters.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
|
||||
|
if (byteCount != quantity * 2) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
// 解析并写入寄存器数据
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
int dataIndex = 7 + i * 2; |
||||
|
short value = (short) (((request[dataIndex] & 0xFF) << 8) | (request[dataIndex + 1] & 0xFF)); |
||||
|
|
||||
|
int registerAddress = startAddress + i; |
||||
|
holdingRegisters[registerAddress] = value; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
byte[] response = createWriteMultipleResponse(request[1], startAddress, quantity); |
||||
|
Log.d(TAG, "Wrote " + quantity + " registers from address " + startAddress); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleWriteMultipleCoils(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
int byteCount = request[6] & 0xFF; |
||||
|
|
||||
|
Log.d(TAG, "Write Multiple Coils - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
if (quantity < 1 || quantity > 1968) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > coils.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
|
||||
|
int expectedByteCount = (quantity + 7) / 8; |
||||
|
if (byteCount != expectedByteCount) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
// 解析并写入线圈数据
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
int byteIndex = 7 + (i / 8); |
||||
|
int bitIndex = i % 8; |
||||
|
boolean value = ((request[byteIndex] >> bitIndex) & 0x01) == 0x01; |
||||
|
|
||||
|
int coilAddress = startAddress + i; |
||||
|
coils[coilAddress] = value; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
byte[] response = createWriteMultipleResponse(request[1], startAddress, quantity); |
||||
|
Log.d(TAG, "Wrote " + quantity + " coils from address " + startAddress); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] createWriteMultipleResponse(byte functionCode, int startAddress, int quantity) { |
||||
|
byte[] response = new byte[8]; |
||||
|
|
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = functionCode; |
||||
|
response[2] = (byte) (startAddress >> 8); |
||||
|
response[3] = (byte) (startAddress & 0xFF); |
||||
|
response[4] = (byte) (quantity >> 8); |
||||
|
response[5] = (byte) (quantity & 0xFF); |
||||
|
|
||||
|
addCrc(response); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] createExceptionResponse(byte functionCode, byte exceptionCode) { |
||||
|
byte[] response = new byte[5]; |
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = (byte) (functionCode | 0x80); |
||||
|
response[2] = exceptionCode; |
||||
|
addCrc(response); |
||||
|
|
||||
|
Log.w(TAG, "Exception response - Function: 0x" + String.format("%02X", functionCode) + |
||||
|
", Code: " + exceptionCode); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleReadDiscreteInputs(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
|
||||
|
Log.d(TAG, "Read Discrete Inputs - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
if (quantity < 1 || quantity > 2000) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > discreteInputs.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
|
||||
|
// 读离散输入响应结构:
|
||||
|
// [slaveId] + [function] + [byteCount] + [data] + [CRC]
|
||||
|
// 1字节 + 1字节 + 1字节 + N字节 + 2字节
|
||||
|
|
||||
|
|
||||
|
int byteCount = (quantity + 7) / 8; |
||||
|
byte[] response = new byte[3 + byteCount + 2]; |
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = request[1]; |
||||
|
response[2] = (byte) byteCount; |
||||
|
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
if (discreteInputs[startAddress + i]) { |
||||
|
response[3 + i / 8] |= (1 << (i % 8)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addCrc(response); |
||||
|
Log.d(TAG, "Read " + quantity + " discrete inputs from address " + startAddress); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleReadInputRegisters(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
|
||||
|
Log.d(TAG, "Read Input Registers - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
if (quantity < 1 || quantity > 125) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > inputRegisters.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
// 读输入寄存器响应结构:
|
||||
|
// [slaveId] + [function] + [byteCount] + [data] + [CRC]
|
||||
|
// 1字节 + 1字节 + 1字节 + (quantity × 2) + 2字节
|
||||
|
|
||||
|
byte[] response = new byte[3 + quantity * 2 + 2]; |
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = request[1]; |
||||
|
response[2] = (byte) (quantity * 2); |
||||
|
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
short value = inputRegisters[startAddress + i]; |
||||
|
response[3 + i * 2] = (byte) (value >> 8); |
||||
|
response[3 + i * 2 + 1] = (byte) value; |
||||
|
} |
||||
|
|
||||
|
addCrc(response); |
||||
|
Log.d(TAG, "Read " + quantity + " input registers from address " + startAddress); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleReadHoldingRegisters(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
|
||||
|
Log.d(TAG, "Read Holding Registers - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
// 验证参数
|
||||
|
if (quantity < 1 || quantity > 125) { |
||||
|
Log.w(TAG, "Invalid quantity: " + quantity); |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); // Illegal data value
|
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > holdingRegisters.length) { |
||||
|
Log.w(TAG, "Address out of range: " + startAddress); |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); // Illegal data address
|
||||
|
} |
||||
|
|
||||
|
// 读保持寄存器响应数据结构:
|
||||
|
// [slaveId] + [function] + [byteCount] + [data] + [CRC]
|
||||
|
// 1字节 + 1字节 + 1字节 + (quantity × 2) + 2字节
|
||||
|
byte[] response = new byte[1 + 1 + 1 + quantity * 2 + 2]; // slaveId + function + byteCount + data + CRC
|
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = request[1]; // Function code
|
||||
|
response[2] = (byte) (quantity * 2); // Byte count
|
||||
|
|
||||
|
// 填充寄存器数据
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
short value = holdingRegisters[startAddress + i]; |
||||
|
response[3 + i * 2] = (byte) (value >> 8); |
||||
|
response[3 + i * 2 + 1] = (byte) value; |
||||
|
} |
||||
|
|
||||
|
// 添加CRC
|
||||
|
addCrc(response); |
||||
|
|
||||
|
Log.d(TAG, "Read " + quantity + " holding registers from address " + startAddress); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleWriteSingleRegister(byte[] request, int length) { |
||||
|
int address = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
short value = (short) (((request[4] & 0xFF) << 8) | (request[5] & 0xFF)); |
||||
|
|
||||
|
Log.d(TAG, "Write Single Register - Address: " + address + ", Value: " + value); |
||||
|
|
||||
|
if (address >= holdingRegisters.length) { |
||||
|
Log.w(TAG, "Register address out of range: " + address); |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); // Illegal data address
|
||||
|
} |
||||
|
|
||||
|
// 更新寄存器值
|
||||
|
holdingRegisters[address] = value; |
||||
|
|
||||
|
|
||||
|
// 写单个寄存器响应结构:
|
||||
|
// [slaveId] + [function] + [address] + [value] + [CRC]
|
||||
|
// 1字节 + 1字节 + 2字节 + 2字节 + 2字节
|
||||
|
byte[] response = new byte[8]; |
||||
|
System.arraycopy(request, 0, response, 0, 6); |
||||
|
response[0] = (byte) slaveId; |
||||
|
addCrc(response); |
||||
|
|
||||
|
|
||||
|
Log.d(TAG, "Register " + address + " updated to: " + value); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleReadCoils(byte[] request, int length) { |
||||
|
int startAddress = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
int quantity = ((request[4] & 0xFF) << 8) | (request[5] & 0xFF); |
||||
|
|
||||
|
Log.d(TAG, "Read Coils - Start: " + startAddress + ", Quantity: " + quantity); |
||||
|
|
||||
|
if (quantity < 1 || quantity > 2000) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x03); |
||||
|
} |
||||
|
|
||||
|
if (startAddress + quantity > coils.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
|
||||
|
// 读线圈响应结构:
|
||||
|
// [slaveId] + [function] + [byteCount] + [data] + [CRC]
|
||||
|
// 1字节 + 1字节 + 1字节 + N字节 + 2字节
|
||||
|
|
||||
|
// 数据字节数计算:N = ceil(quantity / 8)
|
||||
|
// int byteCount = (quantity + 7) / 8; // 向上取整
|
||||
|
// int totalBytes = 1 + 1 + 1 + byteCount + 2; // 5 + byteCount
|
||||
|
|
||||
|
|
||||
|
int byteCount = (quantity + 7) / 8; |
||||
|
byte[] response = new byte[3 + byteCount + 2]; // slaveId + function + byteCount + data + CRC
|
||||
|
response[0] = (byte) slaveId; |
||||
|
response[1] = request[1]; |
||||
|
response[2] = (byte) byteCount; |
||||
|
|
||||
|
// 打包线圈状态到字节
|
||||
|
for (int i = 0; i < quantity; i++) { |
||||
|
if (coils[startAddress + i]) { |
||||
|
response[3 + i / 8] |= (1 << (i % 8)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addCrc(response); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static byte[] handleWriteSingleCoil(byte[] request, int length) { |
||||
|
int address = ((request[2] & 0xFF) << 8) | (request[3] & 0xFF); |
||||
|
boolean value = (request[4] & 0xFF) == 0xFF; |
||||
|
|
||||
|
Log.d(TAG, "Write Single Coil - Address: " + address + ", Value: " + value); |
||||
|
|
||||
|
if (address >= coils.length) { |
||||
|
return createExceptionResponse(request[1], (byte) 0x02); |
||||
|
} |
||||
|
|
||||
|
coils[address] = value; |
||||
|
|
||||
|
|
||||
|
// 写单个线圈响应结构:
|
||||
|
// [slaveId] + [function] + [address] + [value] + [CRC]
|
||||
|
// 1字节 + 1字节 + 2字节 + 2字节 + 2字节 固定 8 字节 byte[] response = new byte[1 + 1 + 2 + 2 + 2]; //
|
||||
|
|
||||
|
byte[] response = new byte[8]; |
||||
|
System.arraycopy(request, 0, response, 0, 6); |
||||
|
response[0] = (byte) slaveId; |
||||
|
addCrc(response); |
||||
|
|
||||
|
Log.d(TAG, "Coil " + address + " updated to: " + value); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
public static boolean validateCrc(byte[] data, int length) { |
||||
|
if (length < 2) return false; |
||||
|
|
||||
|
int calculatedCrc = calculateCrc(data, 0, length - 2); |
||||
|
int receivedCrc = (data[length - 1] & 0xFF) << 8 | (data[length - 2] & 0xFF); |
||||
|
|
||||
|
boolean valid = calculatedCrc == receivedCrc; |
||||
|
if (!valid) { |
||||
|
Log.w(TAG, "CRC mismatch - Calculated: " + calculatedCrc + ", Received: " + receivedCrc); |
||||
|
} |
||||
|
|
||||
|
return valid; |
||||
|
} |
||||
|
|
||||
|
public static void addCrc(byte[] data) { |
||||
|
int crc = calculateCrc(data, 0, data.length - 2); |
||||
|
data[data.length - 2] = (byte) (crc & 0xFF); |
||||
|
data[data.length - 1] = (byte) ((crc >> 8) & 0xFF); |
||||
|
} |
||||
|
|
||||
|
public static int calculateCrc(byte[] data, int start, int length) { |
||||
|
int crc = 0xFFFF; |
||||
|
for (int i = start; i < start + length; i++) { |
||||
|
crc ^= (data[i] & 0xFF); |
||||
|
for (int j = 0; j < 8; j++) { |
||||
|
if ((crc & 0x0001) != 0) { |
||||
|
crc >>= 1; |
||||
|
crc ^= 0xA001; |
||||
|
} else { |
||||
|
crc >>= 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return crc; |
||||
|
} |
||||
|
|
||||
|
public static void sendResponse(byte[] response) { |
||||
|
if (_serialPortHelper == null) { |
||||
|
return; |
||||
|
} |
||||
|
_serialPortHelper.SendData(response); |
||||
|
Log.d(TAG, "Sent response: " + response.length + " bytes"); |
||||
|
} |
||||
|
|
||||
|
// 公共API方法
|
||||
|
public static void updateHoldingRegister(int address, short value) { |
||||
|
if (address >= 0 && address < holdingRegisters.length) { |
||||
|
holdingRegisters[address] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static short getHoldingRegister(int address) { |
||||
|
if (address >= 0 && address < holdingRegisters.length) { |
||||
|
return holdingRegisters[address]; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public static void updateCoil(int address, boolean state) { |
||||
|
if (address >= 0 && address < coils.length) { |
||||
|
coils[address] = state; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static boolean getCoil(int address) { |
||||
|
if (address >= 0 && address < coils.length) { |
||||
|
return coils[address]; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,200 @@ |
|||||
|
package com.example.fivewheel.services; |
||||
|
|
||||
|
import android.graphics.Color; |
||||
|
import android.view.Gravity; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import com.example.fivewheel.MainActivity; |
||||
|
import com.example.fivewheel.models.BspIV; |
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
|
||||
|
public class ReceiivedIVHandler { |
||||
|
|
||||
|
private static final int Buttons_Not_Reset = 0; |
||||
|
private static final int Not_Intialized = 1; |
||||
|
private static final int Move_Halt = 2; |
||||
|
private static final int Move_Forward = 3; |
||||
|
private static final int Move_Backward = 4; |
||||
|
private static final int Move_TurnLeft = 5; |
||||
|
private static final int Move_TurnRight = 6; |
||||
|
private static final int Emergency_Stop = 7; |
||||
|
private static final int Upper_Computer_TakenOver = 8; |
||||
|
private static final int Sbus_Not_Online = 9; |
||||
|
private static final int Android_Down = 10; |
||||
|
|
||||
|
private static final int Cruise_Ahead = 11; |
||||
|
private static final int Cruise_Back = 12; |
||||
|
|
||||
|
public static void midToast(String str, int showTime, MainActivity MainActivity) { |
||||
|
Toast toast = Toast.makeText(MainActivity, str, showTime); |
||||
|
toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0); //设置显示位置
|
||||
|
TextView v = (TextView) toast.getView().findViewById(android.R.id.message); |
||||
|
v.setTextColor(Color.YELLOW); //设置字体颜色
|
||||
|
toast.show(); |
||||
|
} |
||||
|
|
||||
|
public static com.example.fivewheel.models.BspIV.IV_struct_define _toReceiveIV = BspIV.IV_struct_define.newBuilder().build(); |
||||
|
public static BspIV.IV_struct_define _toReceiveIV_Temp = BspIV.IV_struct_define.newBuilder().build(); |
||||
|
|
||||
|
public static void HandleIVData(MainActivity MainActivity, byte[] data) { |
||||
|
try { |
||||
|
|
||||
|
if(data.length<5) return; |
||||
|
if ((data[0] != 0x55) && (data[1] != 0x55)) return;//开头结尾为0x55 表示PV数据
|
||||
|
byte[] crcbytes = new byte[data.length - 2]; |
||||
|
System.arraycopy(data, 0, crcbytes, 0, data.length - 2); |
||||
|
byte[] crc = ModbusCRC.calculateCRC(crcbytes); |
||||
|
|
||||
|
|
||||
|
//这里的校验和C#的校验正好反了
|
||||
|
if (data[data.length - 2] != (byte) (crc[1] & 0xff)) return; |
||||
|
if (data[data.length - 1] != (byte) (crc[0] & 0xff)) return; //crc校验
|
||||
|
// if ((data[0] != 0x55) && (data[1] != 0x55)) return;//开头结尾为0x55 表示PV数据
|
||||
|
|
||||
|
byte[] bytes = new byte[data.length - 4]; |
||||
|
System.arraycopy(data, 2, bytes, 0, data.length - 4); |
||||
|
|
||||
|
try { |
||||
|
_toReceiveIV_Temp = BspIV.IV_struct_define.parseFrom(bytes); |
||||
|
|
||||
|
} catch (InvalidProtocolBufferException ex) { |
||||
|
return; |
||||
|
} |
||||
|
HandleIV(MainActivity, _toReceiveIV_Temp); |
||||
|
} catch ( |
||||
|
Exception e) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static int restoreOriginalError(int errorFlag) { |
||||
|
return Integer.reverseBytes(errorFlag); |
||||
|
} |
||||
|
|
||||
|
public static void HandleIV(MainActivity MainActivity, BspIV.IV_struct_define _toReceiveIV_Temp) { |
||||
|
if (_toReceiveIV_Temp == null) return; |
||||
|
|
||||
|
//若单片机重启,则变量时间戳重置
|
||||
|
if (_toReceiveIV_Temp.getRobotRestart() == 1) { |
||||
|
_toReceiveIV = _toReceiveIV.toBuilder().setTimeStamp(0).build(); |
||||
|
//告知单片机接收到
|
||||
|
MainActivity._toSendPV = MainActivity._toSendPV.toBuilder().setRobotRestartAccepted(1).build(); |
||||
|
return; |
||||
|
} |
||||
|
MainActivity._toSendPV = MainActivity._toSendPV.toBuilder().setRobotRestartAccepted(0).build(); |
||||
|
|
||||
|
|
||||
|
if (_toReceiveIV.getTimeStamp() > _toReceiveIV_Temp.getTimeStamp()) { |
||||
|
return; |
||||
|
|
||||
|
} |
||||
|
_toReceiveIV = _toReceiveIV_Temp; |
||||
|
MainActivity.runOnUiThread(() -> { |
||||
|
MainActivity.mainBinding.rFAngleRoll.setText(String.valueOf(_toReceiveIV.getRobotAngleRoll() / 100.0)); |
||||
|
MainActivity.mainBinding.tvRobotError.setText(String.valueOf(_toReceiveIV.getRobotError())); |
||||
|
MainActivity.mainBinding.tvDynamometer.setText(String.valueOf(_toReceiveIV.getRobotDynamometerValue() / 100.0)); |
||||
|
MainActivity.mainBinding.tvRobotRightCompensation.setText(String.valueOf(_toReceiveIV.getRobotCompensationRight() / 100.0)); |
||||
|
MainActivity.mainBinding.tvRobotLeftCompensation.setText(String.valueOf(_toReceiveIV.getRobotCompensationLeft() / 100.0)); |
||||
|
MainActivity.mainBinding.tvForce.setText(String.valueOf(_toReceiveIV.getRobotForceValue())); |
||||
|
MainActivity.mainBinding.tvRobotCurrent.setText("L" + String.valueOf(_toReceiveIV.getRobotCurrentLeft() / 1000) |
||||
|
+ "R" + String.valueOf(_toReceiveIV.getRobotCurrentRight() / 1000)); |
||||
|
|
||||
|
int leftError = _toReceiveIV.getRobotErrorLeft(); |
||||
|
int rightError = _toReceiveIV.getRobotErrorRight(); |
||||
|
|
||||
|
// 还原成原始 MCU 的错误值
|
||||
|
int leftOriginal = restoreOriginalError(leftError); |
||||
|
int rightOriginal = restoreOriginalError(rightError); |
||||
|
|
||||
|
// 左
|
||||
|
if (leftOriginal != 0) { |
||||
|
StringBuilder leftBits = new StringBuilder("错误: "); |
||||
|
for (int i = 0; i < 32; i++) { |
||||
|
if (((leftOriginal >> i) & 1) == 1) { |
||||
|
leftBits.append(32 - i).append(" "); |
||||
|
} |
||||
|
} |
||||
|
MainActivity.mainBinding.tvLeftError.setText(leftBits.toString().trim()); |
||||
|
} else { |
||||
|
MainActivity.mainBinding.tvLeftError.setText("正常"); |
||||
|
} |
||||
|
|
||||
|
//右
|
||||
|
if (rightOriginal != 0) { |
||||
|
StringBuilder rightBits = new StringBuilder("错误: "); |
||||
|
for (int i = 0; i < 32; i++) { |
||||
|
if (((rightOriginal >> i) & 1) == 1) { |
||||
|
rightBits.append(32 - i).append(" "); |
||||
|
} |
||||
|
} |
||||
|
MainActivity.mainBinding.tvRightError.setText(rightBits.toString().trim()); |
||||
|
} else { |
||||
|
MainActivity.mainBinding.tvRightError.setText("正常"); |
||||
|
} |
||||
|
|
||||
|
if (_toReceiveIV.getRobotError() != 0 && _toReceiveIV.getRobotCurrentState() != 12) { |
||||
|
|
||||
|
} else { |
||||
|
String m = ""; |
||||
|
switch (_toReceiveIV.getRobotCurrentState()) { |
||||
|
case 0: |
||||
|
m = "停止"; |
||||
|
break; |
||||
|
case 1: |
||||
|
m = "前进"; |
||||
|
break; |
||||
|
case 2: |
||||
|
m = "后退"; |
||||
|
break; |
||||
|
case 3: |
||||
|
m = "左转"; |
||||
|
break; |
||||
|
case 4: |
||||
|
m = "右转"; |
||||
|
break; |
||||
|
case 5: |
||||
|
m = "自动前进"; |
||||
|
break; |
||||
|
case 6: |
||||
|
m = "自动后退"; |
||||
|
break; |
||||
|
case 7: |
||||
|
m = "左换道"; |
||||
|
break; |
||||
|
case 8: |
||||
|
m = "右换道"; |
||||
|
break; |
||||
|
case 9: |
||||
|
m = "上换道"; |
||||
|
break; |
||||
|
case 10: |
||||
|
m = "下换道"; |
||||
|
break; |
||||
|
case 11: |
||||
|
m = "换道完成"; |
||||
|
break; |
||||
|
case 12: |
||||
|
m = "急停"; |
||||
|
break; |
||||
|
default: |
||||
|
throw new IllegalStateException("Unexpected value: " + _toReceiveIV.getRobotCurrentState()); |
||||
|
} |
||||
|
MainActivity.mainBinding.tvRobotError.setText(m); |
||||
|
} |
||||
|
int errorInt = _toReceiveIV.getSystemError(); |
||||
|
|
||||
|
String errorString = ErrorDeocdeHelper.ErrorDeocde(errorInt); |
||||
|
|
||||
|
String error_to_Display = "错误:"; |
||||
|
if (_toReceiveIV.getRobotErrorLeft() != 0) { |
||||
|
errorString += " \t 电机1错误码:" + String.valueOf(_toReceiveIV.getRobotErrorLeft()); |
||||
|
} |
||||
|
if (_toReceiveIV.getRobotErrorRight() != 0) { |
||||
|
errorString += " \t 电机2错误码:" + String.valueOf(_toReceiveIV.getRobotErrorRight()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,6 +1,2 @@ |
|||||
cd /d D:\Android_studio_workspace\RemoveMarineAnimals\app\src\main\java |
protoc --java_out . *.proto |
||||
protoc --proto_path=. --java_out=. bsp_IV.proto |
|
||||
protoc --proto_path=. --java_out=. bsp_PV.proto |
|
||||
protoc --proto_path=. --java_out=. bsp_Error.proto |
|
||||
pause |
|
||||
|
|
||||
|
|||||
|
After Width: | Height: | Size: 162 KiB |
@ -0,0 +1,24 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
|
||||
|
<!-- 设置透明背景色 --> |
||||
|
<solid android:color="#00ffffff" /> |
||||
|
|
||||
|
<!-- 设置一个黑色边框 --> |
||||
|
<stroke |
||||
|
android:width="16px" |
||||
|
android:color="#ffffff" /> |
||||
|
<!-- 设置四个圆角的半径 --> |
||||
|
<corners |
||||
|
android:bottomLeftRadius="10px" |
||||
|
android:bottomRightRadius="10px" |
||||
|
android:topLeftRadius="10px" |
||||
|
android:topRightRadius="10px" /> |
||||
|
<!-- 设置一下边距,让空间大一点 --> |
||||
|
<!-- <padding--> |
||||
|
<!-- android:bottom="5dp"--> |
||||
|
<!-- android:left="5dp"--> |
||||
|
<!-- android:right="5dp"--> |
||||
|
<!-- android:top="5dp" />--> |
||||
|
|
||||
|
</shape> |
||||
|
After Width: | Height: | Size: 902 KiB |
Loading…
Reference in new issue