package org.openlcb.implementations;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;
import org.openlcb.NodeID;
/**
* Service for reading and writing via the Memory Configuration protocol
*
* Meant to shield the using code from all the details of that
* process via read and write primitives.
*
* Can accept requests without the using code having to serialize them.
*
* @author Bob Jacobsen Copyright 2012
* @author David Harris Copyright 2016
* @version $Revision: -1 $
*/
public class MemoryConfigurationService {
private static final int DATAGRAM_TYPE = 0x20;
/**
* @param downstream Connection in the direction of the layout
*/
public MemoryConfigurationService(NodeID here, DatagramService downstream) {
this.here = here;
this.downstream = downstream;
// connect to be notified of config service
downstream.registerForReceive(new DatagramService.DatagramServiceReceiveMemo(DATAGRAM_TYPE){
// Process a datagram received here; previous request state part of decoding
//
// does not allow for overlapping operations, either to a single node nor or multiple types
// nor to multiple nodes
//
// doesn't check for match of reply to memo, but eventually should.
@Override
public void handleData(NodeID dest, int[] data, DatagramService.ReplyMemo service) {
//log System.out.println("OLCB: handleData");
service.acceptData(0);
if (readMemo != null) {
// figure out address space uses byte?
boolean spaceByte = ((data[1] & 0x03) == 0);
byte[] content;
if ((data[1]&0x08) == 0) {
// normal read reply
content = new byte[data.length-6+(spaceByte ? -1 : 0)];
for (int i = 0; i= 11)
lowAddress = (((long)data[8]&0xFF)<<24)|(((long)data[9]&0xFF)<<16)|(((long)data[10]&0xFF)<<8)|((long)data[11]&0xFF);
McsAddrSpaceMemo memo = addrSpaceMemo;
addrSpaceMemo = null;
memo.handleAddrSpaceData(dest, space, highAddress, lowAddress, flags, "");
}
// config memo may trigger address space read, so do second
if (configMemo != null) {
// doesn't handle decode of name string, but should
int commands = (data[2]<<8)+data[3];
int options = data[4];
int highSpace = data[5];
int lowSpace = data[6];
McsConfigMemo memo = configMemo;
configMemo = null;
memo.handleConfigData(dest, commands, options, highSpace, lowSpace,"");
}
if (writeMemo != null) {
// needs code to handle delayed reply
System.err.println("MemoryConfiguration Service: Code for delayed reply not yet present"); //log
McsWriteMemo memo = writeMemo;
writeMemo = null;
//memo.handleConfigData(dest, commands, options, highSpace, lowSpace,"");
}
// dph
if (writeStreamMemo != null) {
// receive destinationStreamID
// figure out address space uses byte?
boolean spaceByte = ((data[1] & 0x03) == 0);
byte[] content;
if ((data[1]&0x08) == 0) {
// normal read reply
content = new byte[data.length-6+(spaceByte ? -1 : 0)];
for (int i = 0; i= 0xFD) this.data[1] |= space&0x3;
this.data[2] = (int)(address>>24)&0xFF;
this.data[3] = (int)(address>>16)&0xFF;
this.data[4] = (int)(address>>8 )&0xFF;
this.data[5] = (int)(address )&0xFF;
if (spaceByte) this.data[6] = space;
this.data[6+(spaceByte ? 1 : 0)] = count;
this.memo = memo;
}
McsReadMemo memo;
@Override
public void handleReply(int code) {
memo.handleWriteReply(code);
}
}
@Immutable
@ThreadSafe
static public class McsWriteMemo {
public McsWriteMemo(NodeID dest, int space, long address, byte[] data) {
this.data = data;
this.address = address;
this.space = space;
this.dest = dest;
}
byte[] data;
final long address;
final int space;
final NodeID dest;
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (! (o instanceof McsWriteMemo)) return false;
McsWriteMemo m = (McsWriteMemo) o;
if (this.dest != m.dest) return false;
if (this.space != m.space) return false;
if (this.address != m.address) return false;
if (this.data.length != m.data.length) return false;
for (int i = 0; i < this.data.length; i++)
if (this.data[i] != m.data[i]) return false;
return true;
}
@Override
public String toString() {
return "McsWriteMemo: "+address;
}
@Override
public int hashCode() { return this.data.length+this.data[0]+dest.hashCode()+((int)address)+space; }
/**
* @param code 0 for OK, non-zero for error reply
*/
public void handleWriteReply(int code) {
}
}
@Immutable
@ThreadSafe
static public class WriteDatagramMemo extends DatagramService.DatagramServiceTransmitMemo {
WriteDatagramMemo(NodeID dest, int space, long address, byte[] content, McsWriteMemo memo) {
super(dest);
boolean spaceByte = false;
if (space<0xFD) spaceByte = true;
this.data = new int[6+(spaceByte ? 1 : 0)+content.length];
this.data[0] = DATAGRAM_TYPE;
this.data[1] = 0x00;
if (space >= 0xFD) this.data[1] |= space&0x3;
this.data[2] = (int)(address>>24)&0xFF;
this.data[3] = (int)(address>>16)&0xFF;
this.data[4] = (int)(address>>8 )&0xFF;
this.data[5] = (int)(address )&0xFF;
if (spaceByte) this.data[6] = space;
for (int i = 0; i < content.length; i++)
this.data[6+(spaceByte ? 1 : 0)+i] = content[i];
this.memo = memo;
}
McsWriteMemo memo;
/**
* Handle immediate write reply from datagram.
* Should refer back to the memo request
*/
public void handleReply(int code) {
memo.handleWriteReply(code);
}
}
// dph
// need sourceStreamID
@Immutable
@ThreadSafe
static public class WriteStreamMemo extends DatagramService.DatagramServiceTransmitMemo {
// public WriteStreamMemo(NodeID dest, int space, long address, byte[] content, McsWriteMemo memo) {
public WriteStreamMemo(NodeID dest, int space, long address, McsWriteStreamMemo memo) {
super(dest);
this.space=space;
this.address = address;
boolean spaceByte = false;
if (space<0xFD) spaceByte = true;
this.data = new int[6+(spaceByte ? 1 : 0)+1];
this.data[0] = DATAGRAM_TYPE;
this.data[1] = 0x20;
if (space >= 0xFD) this.data[1] |= space&0x3;
this.data[2] = (int)(address>>24)&0xFF;
this.data[3] = (int)(address>>16)&0xFF;
this.data[4] = (int)(address>>8 )&0xFF;
this.data[5] = (int)(address )&0xFF;
if (spaceByte) {
this.data[6] = space;
this.data[7] = 0x04;
} else this.data[6] = 0x04; // srcStreamID???? why do we need this?
//for (int i = 0; i < content.length; i++)
// this.data[6+(spaceByte ? 1 : 0)+i] = content[i];
this.memo = memo;
}
int space;
long address;
McsWriteStreamMemo memo;
/**
* Handle immediate write reply from datagram.
* Should refer back to the memo request
*/
public void handleReply(int code) {
memo.handleWriteReply(code);
}
}
@Immutable
@ThreadSafe
static public class McsConfigMemo {
public McsConfigMemo(NodeID dest) {
this.dest = dest;
}
final NodeID dest;
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (! (o instanceof McsConfigMemo)) return false;
McsConfigMemo m = (McsConfigMemo) o;
return this.dest == m.dest;
}
@Override
public String toString() {
return "McsConfigMemo";
}
@Override
public int hashCode() { return dest.hashCode(); }
/**
* Overload this for notification of failure reply
* @param code non-zero for error reply
*/
public void handleWriteReply(int code) {
}
/**
* Overload this for notification of data.
*/
public void handleConfigData(NodeID dest, int commands, int options, int highSpace, int lowSpace, String name) {
}
}
@Immutable
@ThreadSafe
static public class ConfigDatagramMemo extends DatagramService.DatagramServiceTransmitMemo {
ConfigDatagramMemo(NodeID dest, McsConfigMemo memo) {
super(dest);
this.data = new int[2];
this.data[0] = DATAGRAM_TYPE;
this.data[1] = 0x80;
this.memo = memo;
}
McsConfigMemo memo;
public void handleReply(int code) {
memo.handleWriteReply(code);
}
}
@Immutable
@ThreadSafe
static public class McsAddrSpaceMemo {
public McsAddrSpaceMemo(NodeID dest, int space) {
this.dest = dest;
this.space = space;
}
NodeID dest;
int space;
public boolean equals(Object o) {
if (o == null) return false;
if (! (o instanceof McsAddrSpaceMemo)) return false;
McsAddrSpaceMemo m = (McsAddrSpaceMemo) o;
if (this.space != m.space) return false;
return this.dest == m.dest;
}
public String toString() {
return "McsAddrSpaceMemo "+space;
}
public int hashCode() { return dest.hashCode()+space; }
/**
* Overload this for notification of failure reply
* @param code non-zero for error reply
*/
public void handleWriteReply(int code) {
}
/**
* Overload this for notification of data.
*/
public void handleAddrSpaceData(NodeID dest, int space, long hiAddress, long lowAddress, int flags, String desc) {
}
}
@Immutable
@ThreadSafe
static public class AddrSpaceDatagramMemo extends DatagramService.DatagramServiceTransmitMemo {
AddrSpaceDatagramMemo(NodeID dest, McsAddrSpaceMemo memo) {
super(dest);
this.data = new int[3];
this.data[0] = DATAGRAM_TYPE;
this.data[1] = 0x84;
this.data[2] = memo.space;
this.memo = memo;
}
McsAddrSpaceMemo memo;
public void handleReply(int code) {
memo.handleWriteReply(code);
}
}
}