514 lines
12 KiB
Java
514 lines
12 KiB
Java
package org.hwo.servicelink;
|
||
|
||
import static org.hwo.logging.Logging.log;
|
||
|
||
import java.awt.DisplayMode;
|
||
import java.io.IOException;
|
||
import java.nio.channels.ShutdownChannelGroupException;
|
||
import java.util.Hashtable;
|
||
import java.util.LinkedList;
|
||
import java.util.List;
|
||
|
||
import org.hwo.ByteArrayHelper;
|
||
import org.hwo.ByteArrayHexlifier;
|
||
import org.hwo.Smoother;
|
||
import org.hwo.StringHelper;
|
||
import org.hwo.io.NewSerialPort.NewSerialPort;
|
||
import org.hwo.io.NewSerialPort.NewSerialPortListener;
|
||
import org.hwo.servicelink.exceptions.ServiceLinkException;
|
||
import org.hwo.servicelink.exceptions.ServiceLinkRequestFailedException;
|
||
|
||
import static org.hwo.servicelink.ServiceLinkTelegram.*;
|
||
import static org.hwo.logging.LogLevel.*;
|
||
|
||
/* ServiceLink
|
||
*
|
||
* Kommunikation via USB mit RegBus f<>higem System
|
||
*
|
||
* Übertragungssicherheit wird durch USB sichergestellt
|
||
*
|
||
* Übertragen werden Telegramme mit folgendem Aufbau:
|
||
*
|
||
* Offset Type Inhalt
|
||
* 0 Byte RequestTyp ( 0 = NoOP, 1 = Wert lesen, 2 = Wert schreiben, 3 = Ereignis senden )
|
||
* 1 Byte Achse
|
||
* 2 Byte Knoten
|
||
* 3 Short Register Nummer
|
||
* 5 Int32/Float Wert (nur bei schreiben oder lesen antwort)
|
||
*
|
||
*/
|
||
|
||
|
||
public class ServiceLink implements NewSerialPortListener {
|
||
private NewSerialPort
|
||
serialPort;
|
||
|
||
int retries;
|
||
|
||
private ServiceRegisterCache
|
||
serviceRegisterCache;
|
||
private AsynchronServiceLinkProvider
|
||
asynchronServiceLinkProvider;
|
||
|
||
Smoother requestTime;
|
||
int requestTimeOut;
|
||
|
||
boolean useV2 = true;
|
||
boolean forceSynchronousRequests;
|
||
|
||
LinkedList<ServiceLinkTelegram> txQueue;
|
||
Hashtable<ServiceLinkAddress, List<ServiceLinkTelegram>>
|
||
pendingRequests;
|
||
int pendingRequestsCount;
|
||
|
||
RxWorker rxWorker;
|
||
TxWorker txWorker;
|
||
|
||
boolean closing;
|
||
|
||
private List<ServiceLinkListener> serviceLinkListeners;
|
||
|
||
public ServiceLink(NewSerialPort serialPort)
|
||
{
|
||
this.requestTimeOut = 100;
|
||
|
||
this.serviceLinkListeners = new LinkedList<ServiceLinkListener>();
|
||
this.retries = 3;
|
||
this.serialPort = serialPort;
|
||
this.serialPort.setTimeOut(25);
|
||
this.serialPort.addNewSerialPortListener(this);
|
||
this.serviceRegisterCache = new ServiceRegisterCache(this);
|
||
this.asynchronServiceLinkProvider = new AsynchronServiceLinkProvider(serviceRegisterCache);
|
||
this.requestTime = new Smoother();
|
||
this.requestTime.setTn(16);
|
||
|
||
this.txWorker = null;
|
||
this.rxWorker = null;
|
||
|
||
this.txQueue = new LinkedList<>();
|
||
this.pendingRequests = new Hashtable<>();
|
||
|
||
}
|
||
|
||
/** Handling der Seriellen Verbindung **/
|
||
|
||
private void _open(){
|
||
this.txWorker = new TxWorker();
|
||
this.rxWorker = new RxWorker();
|
||
|
||
this.serialPort.setTimeOut(25);
|
||
this.serialPort.open();
|
||
|
||
this.txWorker.start();
|
||
this.rxWorker.start();
|
||
}
|
||
private void _close(){
|
||
log(DEBUG,"servicelink closing connection...");
|
||
log(DEBUG,"txWorker...");
|
||
this.txWorker.exit();
|
||
log(DEBUG,"rxWorker...");
|
||
this.rxWorker.exit();
|
||
|
||
this.txWorker = null;
|
||
this.rxWorker = null;
|
||
|
||
log(DEBUG,"serialPort...");
|
||
this.serialPort.close();
|
||
}
|
||
|
||
public void open() throws ServiceLinkException
|
||
{
|
||
if (!closing){
|
||
close();
|
||
|
||
if (serialPort != null){
|
||
_open();
|
||
} else {
|
||
throwNotOpen();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void close()
|
||
{
|
||
if (!closing){
|
||
if (txWorker != null){
|
||
_close();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void throwNotOpen() throws ServiceLinkException
|
||
{
|
||
if (!isOpen())
|
||
throw new ServiceLinkException("serial port not opened!");
|
||
}
|
||
|
||
public boolean isOpen()
|
||
{
|
||
return (this.serialPort != null) && (this.serialPort.isOpen()) && (this.txWorker != null);
|
||
}
|
||
|
||
/** -- ENDE: Handling der seriellen Verbindung -- **/
|
||
|
||
/** Handling der Queues **/
|
||
|
||
public boolean isUseV2() {
|
||
return useV2;
|
||
}
|
||
public void setUseV2(boolean useV2) {
|
||
this.useV2 = useV2;
|
||
}
|
||
|
||
public boolean isForceSynchronousRequests() {
|
||
return forceSynchronousRequests;
|
||
}
|
||
public void setForceSynchronousRequests(boolean forceSynchronousRequests) {
|
||
this.forceSynchronousRequests = forceSynchronousRequests;
|
||
}
|
||
|
||
private void addPendingRequest(ServiceLinkTelegram request){
|
||
synchronized (pendingRequests) {
|
||
if (!pendingRequests.containsKey(request.getAddress())){
|
||
pendingRequests.put(request.getAddress(),new LinkedList<ServiceLinkTelegram>());
|
||
}
|
||
List<ServiceLinkTelegram> ll = pendingRequests.get(request.getAddress());
|
||
ll.add(request);
|
||
pendingRequestsCount++;
|
||
}
|
||
}
|
||
private void removePendingRequest(ServiceLinkTelegram request){
|
||
synchronized (pendingRequests) {
|
||
if (pendingRequests.containsKey(request.getAddress())){
|
||
pendingRequests.get(request.getAddress()).remove(request);
|
||
pendingRequestsCount--;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void appendRequest(ServiceLinkTelegram request){
|
||
|
||
addPendingRequest(request);
|
||
|
||
synchronized (txQueue) {
|
||
txQueue.add(request);
|
||
txQueue.notify();
|
||
}
|
||
}
|
||
public void removeRequest(ServiceLinkTelegram request){
|
||
synchronized (txQueue) {
|
||
txQueue.remove(request);
|
||
}
|
||
removePendingRequest(request);
|
||
}
|
||
|
||
public void queueRequestNonPending(ServiceLinkTelegram request){
|
||
synchronized (txQueue) {
|
||
txQueue.add(request);
|
||
txQueue.notify();
|
||
}
|
||
}
|
||
|
||
public void queueRequest(ServiceLinkTelegram request){
|
||
long ts_start = System.currentTimeMillis();
|
||
|
||
log(DEBUG,"QUEUE: %s",request);
|
||
|
||
if (forceSynchronousRequests){
|
||
log(DEBUGDETAIL,"SYNCH.");
|
||
while (true){
|
||
synchronized (pendingRequests) {
|
||
if (pendingRequestsCount == 0){
|
||
appendRequest(request);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
appendRequest(request);
|
||
}
|
||
|
||
log(DEBUGDETAIL,"WAIT: %s",request);
|
||
|
||
synchronized (request) {
|
||
try {
|
||
request.wait(requestTimeOut);
|
||
} catch (InterruptedException e) {
|
||
log(e);
|
||
}
|
||
}
|
||
|
||
removeRequest(request);
|
||
|
||
requestTime.cycle( (int)(System.currentTimeMillis() - ts_start) );
|
||
|
||
log(DEBUG,"DONE: %s",request);
|
||
|
||
}
|
||
|
||
public void received(ServiceLinkTelegram rx){
|
||
|
||
synchronized (pendingRequests) {
|
||
if (pendingRequests.containsKey(rx.getAddress())){
|
||
for (ServiceLinkTelegram pending: pendingRequests.get(rx.getAddress())){
|
||
|
||
if ((pending.getOpcode() & (REQ_INT | REQ_FLOAT))==(rx.getOpcode() & (REQ_INT | REQ_FLOAT))){
|
||
synchronized (pending) {
|
||
pending.setValue(rx.getValue());
|
||
pending.notifyAll();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
asynchronServiceLinkProvider.telegramReceived(rx);
|
||
}
|
||
|
||
/** ENDE: Handling der Queues **/
|
||
|
||
public int getRequestTimeOut() {
|
||
return requestTimeOut;
|
||
}
|
||
public void setRequestTimeOut(int requestTimeOut) {
|
||
this.requestTimeOut = requestTimeOut;
|
||
}
|
||
|
||
|
||
|
||
public void addServiceLinkListener(ServiceLinkListener listener){
|
||
serviceLinkListeners.add(listener);
|
||
}
|
||
public void removeServiceLinkListener(ServiceLinkListener listener){
|
||
serviceLinkListeners.remove(listener);
|
||
}
|
||
private void fireConnectionStateChanged(Boolean connected){
|
||
for (ServiceLinkListener l: serviceLinkListeners)
|
||
l.connectionStateChanged(connected);
|
||
}
|
||
|
||
|
||
public int getAverageRequestTime()
|
||
{
|
||
return requestTime.getWert();
|
||
}
|
||
|
||
public ServiceRegisterCache getServiceRegisterCache()
|
||
{
|
||
return this.serviceRegisterCache;
|
||
}
|
||
|
||
public AsynchronServiceLinkProvider getAsynchronServiceLinkProvider() {
|
||
return asynchronServiceLinkProvider;
|
||
}
|
||
|
||
|
||
|
||
@Override
|
||
protected void finalize() throws Throwable {
|
||
if (serialPort.isOpen())
|
||
serialPort.close();
|
||
}
|
||
|
||
public ServiceLinkTelegram createTelegram(int achse,int knoten,int register,int opcode,Object value){
|
||
|
||
ServiceLinkTelegram request = useV2 ?
|
||
new ServiceLinkV2Telegram(opcode, achse, knoten, register, value) :
|
||
new ServiceLinkV1Telegram(opcode, achse, knoten, register, value);
|
||
|
||
return request;
|
||
}
|
||
|
||
public Integer readInt(int achse,int knoten,int register) throws IOException, ServiceLinkException, ServiceLinkRequestFailedException
|
||
{
|
||
ServiceLinkTelegram request = useV2 ?
|
||
new ServiceLinkV2Telegram(REQ_INT | REQ_READ, achse, knoten, register, null) :
|
||
new ServiceLinkV1Telegram(REQ_INT | REQ_READ, achse, knoten, register, null);
|
||
queueRequest(request);
|
||
return (Integer)request.getValue();
|
||
}
|
||
public Float readFloat(int achse,int knoten,int register) throws IOException, ServiceLinkException, ServiceLinkRequestFailedException
|
||
{
|
||
ServiceLinkTelegram request = useV2 ?
|
||
new ServiceLinkV2Telegram(REQ_FLOAT | REQ_READ, achse, knoten, register, null) :
|
||
new ServiceLinkV1Telegram(REQ_FLOAT | REQ_READ, achse, knoten, register, null);
|
||
queueRequest(request);
|
||
return (Float)request.getValue();
|
||
}
|
||
|
||
public void writeInt(int achse,int knoten,int register,int value) throws IOException, ServiceLinkException, ServiceLinkRequestFailedException
|
||
{
|
||
ServiceLinkTelegram request = useV2 ?
|
||
new ServiceLinkV2Telegram(REQ_INT | REQ_WRITE, achse, knoten, register, value) :
|
||
new ServiceLinkV1Telegram(REQ_INT | REQ_WRITE, achse, knoten, register, value);
|
||
queueRequest(request);
|
||
}
|
||
public void writeFloat(int achse,int knoten,int register,float value) throws IOException, ServiceLinkException, ServiceLinkRequestFailedException{
|
||
ServiceLinkTelegram request = useV2 ?
|
||
new ServiceLinkV2Telegram(REQ_FLOAT | REQ_WRITE, achse, knoten, register, value) :
|
||
new ServiceLinkV1Telegram(REQ_FLOAT | REQ_WRITE, achse, knoten, register, value);
|
||
queueRequest(request);
|
||
}
|
||
|
||
public NewSerialPort getSerialPort() {
|
||
return serialPort;
|
||
}
|
||
|
||
public void setSerialPort(NewSerialPort serialPort) {
|
||
if (isOpen()){
|
||
this.close();
|
||
this.serialPort.removeNewSerialPortListener(this);
|
||
}
|
||
|
||
this.serialPort = serialPort;
|
||
this.serialPort.addNewSerialPortListener(this);
|
||
}
|
||
|
||
|
||
|
||
@Override
|
||
public void connectionStateChanged(NewSerialPort port, boolean connected) {
|
||
fireConnectionStateChanged(connected);
|
||
}
|
||
|
||
|
||
private class RxWorker extends Thread {
|
||
private boolean exit;
|
||
|
||
public synchronized void exit(){
|
||
if (!isAlive())
|
||
return;
|
||
|
||
if (exit)
|
||
return;
|
||
|
||
exit = true;
|
||
this.notifyAll();
|
||
try {
|
||
this.wait(getSerialPort().getTimeOut());
|
||
|
||
if (this.isAlive()){
|
||
this.stop();
|
||
}
|
||
|
||
} catch (InterruptedException e) {
|
||
log(e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void run() {
|
||
ServiceLinkTelegram rx;
|
||
|
||
while (true){
|
||
synchronized (this) {
|
||
if (exit){
|
||
this.notifyAll();
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (!isOpen())
|
||
break;
|
||
|
||
try {
|
||
rx = useV2 ?
|
||
new ServiceLinkV2Telegram() :
|
||
new ServiceLinkV1Telegram() ;
|
||
|
||
if (rx.read(getSerialPort().getInputStream())){
|
||
received(rx);
|
||
};
|
||
|
||
} catch (Exception io){
|
||
log(io);
|
||
exit = true;
|
||
ServiceLink.this.close();
|
||
};
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
private class TxWorker extends Thread{
|
||
private boolean exit;
|
||
|
||
public TxWorker(){
|
||
exit = false;
|
||
}
|
||
|
||
public synchronized void exit(){
|
||
if (!isAlive())
|
||
return;
|
||
|
||
if (exit)
|
||
return;
|
||
|
||
exit = true;
|
||
try {
|
||
this.notifyAll();
|
||
this.wait(250);
|
||
|
||
if (this.isAlive())
|
||
this.stop();
|
||
|
||
} catch (InterruptedException e) {
|
||
log(e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void run() {
|
||
ServiceLinkTelegram tx;
|
||
|
||
while (true){
|
||
synchronized (txQueue) {
|
||
try {
|
||
txQueue.wait(100);
|
||
} catch (InterruptedException e) {
|
||
log(e);
|
||
}
|
||
|
||
synchronized (this) {
|
||
if (exit){
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!isOpen())
|
||
break;
|
||
|
||
synchronized (txQueue) {
|
||
tx = txQueue.pollFirst();
|
||
}
|
||
|
||
while ((tx != null)&&(!exit)&&(ServiceLink.this.isOpen())){
|
||
byte[] txbytes = tx.toByteArray();
|
||
try {
|
||
log(DEBUGDETAIL,"TX BYTES: %s",ByteArrayHexlifier.byteArrayToString(txbytes));
|
||
serialPort.getOutputStream().write(txbytes);
|
||
} catch (IOException io) {
|
||
log(io);
|
||
exit = true;
|
||
ServiceLink.this.close();
|
||
}
|
||
|
||
synchronized (txQueue) {
|
||
tx = txQueue.pollFirst();
|
||
}
|
||
}
|
||
}
|
||
|
||
synchronized (this) {
|
||
this.notifyAll();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
}
|