java-org.hwo.servicelink/src/org/hwo/servicelink/ServiceLink.java

514 lines
12 KiB
Java
Raw Blame History

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();
}
}
}
}