package center.system.msg;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import ru.vassaev.core.PrmInterface;
import ru.vassaev.core.exception.SysException;
import ru.vassaev.core.io.ByteMsg;
import ru.vassaev.core.io.CloseableOutputStream;
import ru.vassaev.core.types.StringList;
import ru.vassaev.core.util.Bytes;
import ru.vassaev.core.util.Strings;
public class SAMsg extends ByteMsg {
public static final byte SOH = 0x01;
public static final byte STX = 0x02;
public static final byte EOT = 0x04;
public static final byte ENQ = 0x05;
public static final byte ACK = 0x06;
public static final byte CR = 0x0D;
public static final byte NAK = 0x15;
private int len = 0;
private int lenh = 0;
public static String charset = "ibm866";
private final Map<Integer, byte[]> data = new HashMap<Integer, byte[]>();
private final Map<Integer, byte[]> header = new HashMap<Integer, byte[]>();
public int getType() {
return type;
}
public void reset() {
data.clear();
header.clear();
type = 0;
len = 0;
lenh = 0;
}
private byte type = ENQ;
public ArrayList<byte[]> getMsg() throws SysException {
ArrayList<byte[]> r = new ArrayList<byte[]>();
byte[] add;
byte[] val;
r.add(new byte[] { type });
if (type != STX && type != SOH)
return r;
byte[] la = new byte[2];
r.add(la);
int lhi = 0;
if (type == SOH) {
byte[] lh = new byte[2];
r.add(lh);
for (Map.Entry<Integer, byte[]> e : header.entrySet()) {
val = e.getValue();
add = new byte[3 + val.length];
add[0] = e.getKey().byteValue();
byte[] l = Bytes.intToByte16Revers(val.length);
add[1] = l[0];
add[2] = l[1];
System.arraycopy(val, 0, add, 3, val.length);
lhi += add.length;
r.add(add);
}
byte[] l = Bytes.intToByte16Revers(lhi);
lh[0] = l[0];
lh[1] = l[1];
lhi += 2;
}
for (Map.Entry<Integer, byte[]> e : data.entrySet()) {
val = e.getValue();
add = new byte[3 + val.length];
add[0] = e.getKey().byteValue();
byte[] l = Bytes.intToByte16Revers(val.length);
add[1] = l[0];
add[2] = l[1];
System.arraycopy(val, 0, add, 3, val.length);
lhi += add.length;
r.add(add);
}
byte[] l = Bytes.intToByte16Revers(lhi);
la[0] = l[0];
la[1] = l[1];
int s = 0;
for (int i = 0; i < r.size(); i++) {
s = updateCRC16msb(s, r.get(i));
}
s = ((s<<8) + (s>>8)) & 0xffff;
byte[] crc = Bytes.intToByte16Revers(s);
r.add(crc);
return r;
}
private int updateCRC16msb(int s, int bb) {
int b = bb;
for(int j = 0 ; j < 8 ; j++) {
int x16 = ((((b&0x80) > 0 && (s&0x8000) > 0))
||(((b&0x80) == 0) && ((s&0x8000) == 0))) ? 0 : 1;
int x15 = ((((x16 == 1)&&(s&0x4000) > 0))
||((x16 == 0)&&((s&0x4000) == 0))) ? 0 : 1;
int x2 = ((((x16 == 1)&&(s&0x0002) > 0))
||((x16 == 0)&&((s&0x0002) == 0))) ? 0 : 1;
s = (s << 1) & 0xffff;
b = (b << 1) & 0xffff;
s |= (x16 == 1) ? 0x0001 : 0;
s = (x2 == 1) ? s | 0x0004 : s & 0xfffb;
s = (x15 == 1) ? s | 0x8000 : s & 0x7fff;
}
return s;
}
private int updateCRC16msb(int s, byte pBuf[]) {
int lSize = pBuf.length;
for(int i = 0; i < lSize; i++) {
s = updateCRC16msb(s, pBuf[i] & 0xff);
}
return s;
}
public int getLengthMsg() {
if (type != STX && type != SOH)
return 0;
return (((type == SOH) ? (header.size() * 3) + lenh + 2 : 0) // HEADER (NAME + LEN + D) * N + LENH
+ (data.size() * 3) + len // DATA (NAME + LEN + D) * N
+ 2 // LRC
);
}
public int getLengthFull() {
if (type != STX && type != SOH)
return 1;
return 1 // STX || SOH
+ 2 // LEN
+ getLengthMsg();
}
private void setField0(Integer key, byte[] val) {
byte[] dta = data.remove(key);
if (dta != null)
len = len - dta.length;
if (val != null) {
data.put(key, val);
len += val.length;
}
}
private void setField0(Integer key, String val) throws SysException {
byte[] v = null;
if (val != null) {
try {
v = val.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
}
byte[] dta = data.remove(key);
if (dta != null)
len = len - dta.length;
if (v != null) {
data.put(key, v);
len += v.length;
}
}
private void setFieldH0(Integer key, String val) throws SysException {
byte[] v = null;
if (val != null) {
try {
v = val.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
}
byte[] dta = header.remove(key);
if (dta != null)
lenh = lenh - dta.length;
if (v != null) {
header.put(key, v);
lenh += v.length;
}
}
private void setFieldH0(Integer key, byte[] val){
byte[] dta = header.remove(key);
if (dta != null)
lenh = lenh - dta.length;
if (val != null) {
header.put(key & 0xff, val);
lenh += val.length;
}
}
private final PrmInterface prms = new Prm();
public PrmInterface getPrmInterface() throws SysException {
return prms;
}
public class Prm implements PrmInterface {
public void setField(String key, String val) throws SysException {
if (key == null || key.length() == 0)
return;
if ("type".equals(key)) {
byte type = (byte) (0xff & Strings.parseIntegerNvl(val, STX));
if (type == STX || type == SOH ||
type == EOT || type == ACK || type == CR || type == NAK || type == ENQ)
SAMsg.this.type = type;
else
throw new SysException("The type of message is incorrect");
} else if (key.substring(0, 1).equals("h"))
setFieldH0(Integer.parseInt(key.substring(1)), val);
else
setField0(Integer.parseInt(key), val);
}
public String getField(String key) throws SysException {
if (key == null || key.length() == 0)
return null;
byte[] dta;
if ("type".equals(key)) {
return String.valueOf(type);
} else if (key.substring(0, 1).equals("h"))
dta = header.get(Integer.valueOf(key.substring(1)));
else
dta = data.get(Integer.valueOf(key));
if (dta == null)
return null;
try {
return new String(dta, charset);
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
}
public byte[] getByteField(String key) throws SysException {
if (key == null || key.length() == 0)
return null;
try {
if ("type".equals(key)) {
return String.valueOf(type).getBytes(charset);
} else if (key.substring(0, 1).equals("h"))
return header.get(Integer.valueOf(key.substring(1)));
} catch (UnsupportedEncodingException e) {
throw new SysException(e);
}
return data.get(key);
}
public void clearField(String key) throws SysException {
setField(key, null);
}
public boolean isPresentField(String key) throws SysException {
if (key == null || key.length() == 0)
return false;
if ("type".equals(key)) {
return true;
} else if (key.substring(0, 1).equals("h"))
return header.get(Integer.valueOf(key.substring(1))) != null;
return data.get(Integer.valueOf(key)) != null;
}
public void clearFields() throws SysException {
len = 0;
data.clear();
lenh = 0;
header.clear();
}
public StringList getFieldNames() {
StringList sl = new StringList();
sl.add("type");
if (type == STX || type == SOH) {
for (Integer k : data.keySet())
sl.add(k.toString());
if (type == SOH)
for (Integer k : header.keySet())
sl.add("h" + k);
}
return sl;
}
}
public String getKey() throws SysException {
return null;
}
public void setKey(String key) throws SysException {
}
public CloseableOutputStream getOutputStream() throws IOException {
return new MessageOutputStream();
}
private static enum STEP {TYPE, MSG_LEN, HEADER_LEN, HEADER_FLD, FLD, CRC, NONE};
private class MessageOutputStream extends CloseableOutputStream {
private STEP step = STEP.TYPE; // секция
private int pos = 0; // позиция в секции
private int fld; // код поля
private byte[] ln = new byte[2]; // буфер для длины
private int lmsg = 0;// длина сообщения
private int lhdr = 0;// длина заголовка
private int lfld = 0;// длина поля
private int crc = 0; // контроль
private int ll = 0; // номер записываемого символа
private byte[] data = null; // буфер для значения поля
public void write(int b) throws IOException {
ll ++;
if (b == -1 && !STEP.NONE.equals(step))
throw new IOException("The stream isn't comleted (pos = " + ll + ")");
if (STEP.NONE.equals(step)) // Если поток закрыт
throw new IOException("The stream is already closed");
if (STEP.TYPE.equals(step)) { // Type
type = (byte) (b & 0xff);
if (type == EOT || type == ACK || type == CR || type == NAK || type == ENQ) {
step = STEP.NONE;
} else if (type == STX || type == SOH) {
step = STEP.MSG_LEN;
pos = 0;
} else
throw new IOException("The type of message is incorrect");
crc = updateCRC16msb(crc, type);
} else if (STEP.MSG_LEN.equals(step)) {// msg's len
ln[pos] = (byte) (b & 0xff);
crc = updateCRC16msb(crc, b & 0xff);
pos++;
if (pos == 2) {
lmsg = Bytes.byteToInt(ln);
pos = 0;
if (type == STX)
step = STEP.FLD;
else step = STEP.HEADER_LEN;
}
} else if (STEP.HEADER_LEN.equals(step)) {// header's len
ln[pos] = (byte) (b & 0xff);
crc = updateCRC16msb(crc, b & 0xff);
pos++;
if (pos == 2) {
pos = 0;
lhdr = Bytes.byteToInt(ln);
if (lhdr == 0)
step = STEP.FLD;
else
step = STEP.HEADER_FLD;
}
} else if (STEP.HEADER_FLD.equals(step)) {// Fields in header
byte bt = (byte) (b & 0xff);
crc = updateCRC16msb(crc, bt);
switch (pos) {
case 0: fld = bt; pos++; break;
case 1: ln[0] = bt; pos++; break;
case 2: ln[1] = bt; lfld = Bytes.byteToInt(ln);
if(lfld == 0) {
lhdr -= 3;
lmsg -= 3;
pos = 0;
if (lhdr == 0) {
lmsg = lmsg - 2;
if (lmsg > 0) step = STEP.FLD;
else if (lmsg == 0) step = STEP.CRC;
else throw new IOException("Incorrect message's length");
}
}
else {
pos++;
data = new byte[lfld];
}
break;
default:
lfld--;
data[pos-3] = bt;
if(lfld == 0) {
pos = 0;
lhdr -= (data.length + 3);
lmsg -= (data.length + 3);
SAMsg.this.setFieldH0(fld, data);
data = null;
if (lhdr == 0) {
step = STEP.FLD;
lmsg = lmsg - 2;
}
}
else pos++;
}
} else if(STEP.FLD.equals(step)) {
byte bt = (byte) (b & 0xff);
crc = updateCRC16msb(crc, bt);
switch (pos) {
case 0: fld = bt; pos++; break;
case 1: ln[0] = bt; pos++; break;
case 2: ln[1] = bt; lfld = Bytes.byteToInt(ln);
if(lfld == 0) {
lmsg -= 3;
pos = 0;
if (lmsg == 0) step = STEP.CRC;
else if (lmsg < 0) throw new IOException("Incorrect message's length");
}
else {
pos++;
data = new byte[lfld];
}
break;
default:
lfld--;
data[pos-3] = bt;
if(lfld == 0) {
pos = 0;
lmsg -= (data.length + 3);
SAMsg.this.setField0(fld, data);
data = null;
if (lmsg < 0)
throw new IOException("Incorrect message's lenght");
if (lmsg == 0)
step = STEP.CRC;
}
else pos++;
}
} else if(STEP.CRC.equals(step)) {
ln[pos] = (byte) (b & 0xff);
pos++;
if (pos == 2) {
int lcrc = ((crc<<8) + (crc>>8)) & 0xffff;
if (lcrc != Bytes.byteToInt(ln))
throw new IOException("Incorrect LRC (" + lcrc + " != " + crc + ")");
step = STEP.NONE;
}
}
}
public void close() throws IOException {
step = STEP.NONE;
}
public boolean isClosed() {
return (STEP.NONE.equals(step));
}
}
}