diff --git a/ASN.1 schema/v1_1/ApplicationData.asn1 b/ASN.1 schema/v1_1/ApplicationData.asn1 index b7b4deae..c9449d06 100644 --- a/ASN.1 schema/v1_1/ApplicationData.asn1 +++ b/ASN.1 schema/v1_1/ApplicationData.asn1 @@ -39,8 +39,7 @@ MP_UserLoggingInResp ::= SEQUENCE tokenExpiration Timestamp OPTIONAL, vinList SEQUENCE SIZE(1..256) OF VinInfo OPTIONAL, userPhoto IA5String(SIZE(1..128)) OPTIONAL, - userName IA5String(SIZE(8..12)), - languageType LanguageType OPTIONAL + userName IA5String(SIZE(8..12)) } APPUpgradeInfoReq ::= SEQUENCE { diff --git a/ASN.1 schema/v2_0/ApplicationData.asn1 b/ASN.1 schema/v2_0/ApplicationData.asn1 new file mode 100644 index 00000000..b2ff365b --- /dev/null +++ b/ASN.1 schema/v2_0/ApplicationData.asn1 @@ -0,0 +1,134 @@ +ApplicationDataModule + +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN +MP_SecurityAlarmResp ::= SEQUENCE +{ + securityAlarms SEQUENCE SIZE(1..256) OF SecurityAlarm OPTIONAL +} +OTA_RVMVehicleStatusReq ::= SEQUENCE +{ + vehStatusReqType INTEGER(0..255) +} +OTA_RVMVehicleStatusResp ::= SEQUENCE +{ + statusTime INTEGER(0..2147483647), + gpsPosition RvsPosition(1), + basicVehicleStatus RvsBasicStatus(1), + extendedVehicleStatus RvsExtStatus(1) OPTIONAL +} +OTA_RVCReq ::= SEQUENCE +{ + rvcReqType OCTET STRING(SIZE(1)), + rvcParams SEQUENCE SIZE(1..10) OF RvcReqParam OPTIONAL +} +OTA_RVCStatus ::= SEQUENCE +{ + rvcReqType OCTET STRING(SIZE(1)), + rvcReqSts OCTET STRING(SIZE(1)), + failureType INTEGER(0..255) OPTIONAL, + gpsPosition RvsPosition(1), + basicVehicleStatus RvsBasicStatus(1) +} +SecurityAlarm ::= SEQUENCE +{ + title OCTET STRING(SIZE(1..128)) OPTIONAL, + content OCTET STRING(SIZE(1..2048)) OPTIONAL, + messageType IA5String(SIZE(3)), + vin IA5String(SIZE(17)), + alertId INTEGER(0..65535), + alertTime INTEGER(0..2147483647) OPTIONAL, + latitude INTEGER(-90000000..90000000), + longitude INTEGER(-180000000..180000000) +} +RvsPosition ::= SEQUENCE +{ + wayPoint RvsWayPoint, + timestamp4Short Timestamp4Short, + gpsStatus GPSStatus +} +RvsBasicStatus ::= SEQUENCE +{ + driverDoor BOOLEAN, + passengerDoor BOOLEAN, + rearLeftDoor BOOLEAN, + rearRightDoor BOOLEAN, + bootStatus BOOLEAN, + bonnetStatus BOOLEAN, + lockStatus BOOLEAN, + driverWindow BOOLEAN OPTIONAL, + passengerWindow BOOLEAN OPTIONAL, + rearLeftWindow BOOLEAN OPTIONAL, + rearRightWindow BOOLEAN OPTIONAL, + sunroofStatus BOOLEAN OPTIONAL, + frontRrightTyrePressure INTEGER(0..255) OPTIONAL, + frontLeftTyrePressure INTEGER(0..255) OPTIONAL, + rearRightTyrePressure INTEGER(0..255) OPTIONAL, + rearLeftTyrePressure INTEGER(0..255) OPTIONAL, + wheelTyreMonitorStatus INTEGER(0..255) OPTIONAL, + sideLightStatus BOOLEAN, + dippedBeamStatus BOOLEAN, + mainBeamStatus BOOLEAN, + vehicleAlarmStatus INTEGER(0..255) OPTIONAL, + engineStatus INTEGER(0..255), + powerMode INTEGER(0..255), + lastKeySeen INTEGER(0..65535), + currentjourneyDistance INTEGER(0..65535), + currentJourneyID INTEGER(0..2147483647), + interiorTemperature INTEGER(-128..127), + exteriorTemperature INTEGER(-128..127), + fuelLevelPrc INTEGER(0..255), + fuelRange INTEGER(0..65535), + remoteClimateStatus INTEGER(0..255), + frontLeftSeatHeatLevel INTEGER(0..255) OPTIONAL, + frontRightSeatHeatLevel INTEGER(0..255) OPTIONAL, + canBusActive BOOLEAN, + timeOfLastCANBUSActivity INTEGER(0..2147483647), + clstrDspdFuelLvlSgmt INTEGER(0..255), + mileage INTEGER(0..2147483647), + batteryVoltage INTEGER(0..65535), + extendedData1 INTEGER(0..2147483647) OPTIONAL, + extendedData2 INTEGER(0..2147483647) OPTIONAL +} +RvsExtStatus ::= SEQUENCE +{ + vehicleAlerts SEQUENCE SIZE(0..64) OF VehicleAlertInfo +} +RvcReqParam ::= SEQUENCE +{ + paramId INTEGER(0..65535), + paramValue OCTET STRING(SIZE(1..255)) +} +RvsWayPoint ::= SEQUENCE +{ + position RvsWGS84Point, + heading INTEGER(0..359), + speed INTEGER(-1000..4500), + hdop INTEGER(0..1000), + satellites INTEGER(0..16) +} +Timestamp4Short ::= SEQUENCE +{ + seconds INTEGER(0..2147483647) +} +GPSStatus ::= ENUMERATED +{ + noGpsSignal(0), + timeFix(1), + fix2D(2), + fix3D(3) +} +VehicleAlertInfo ::= SEQUENCE +{ + id INTEGER(0..255), + value INTEGER(0..255) +} +RvsWGS84Point ::= SEQUENCE +{ + latitude INTEGER(-90000000..90000000), + longitude INTEGER(-180000000..180000000), + altitude INTEGER(-100..8900) +} + +END diff --git a/ASN.1 schema/v2_0/MP_DispatcherBody.asn1 b/ASN.1 schema/v2_0/MP_DispatcherBody.asn1 new file mode 100644 index 00000000..30177a4e --- /dev/null +++ b/ASN.1 schema/v2_0/MP_DispatcherBody.asn1 @@ -0,0 +1,33 @@ +MP_DispatcherBodyModule + +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN +MP_DispatcherBody ::= SEQUENCE +{ + uid IA5String(SIZE(50)) OPTIONAL, + token IA5String(SIZE(40)) OPTIONAL, + applicationID IA5String(SIZE(3)), + vin IA5String(SIZE(17)) OPTIONAL, + messageID INTEGER(0..255), + eventCreationTime INTEGER(0..2147483647), + eventID INTEGER(0..2147483647) OPTIONAL, + ulMessageCounter INTEGER(0..65535) OPTIONAL, + dlMessageCounter INTEGER(0..65535) OPTIONAL, + ackMessageCounter INTEGER(0..65535) OPTIONAL, + ackRequired BOOLEAN OPTIONAL, + applicationDataLength INTEGER(0..65535) OPTIONAL, + applicationDataEncoding DataEncodingType OPTIONAL, + applicationDataProtocolVersion INTEGER(0..65535) OPTIONAL, + testFlag INTEGER(1..3) OPTIONAL, + result INTEGER(0..65535) OPTIONAL, + errorMessage OCTET STRING(SIZE(1..1024)) OPTIONAL +} +DataEncodingType ::= ENUMERATED +{ + perUnaligned(0), + der(1), + ber(2) +} + +END diff --git a/ASN.1 schema/v2_0/MP_DispatcherHeader.asn1 b/ASN.1 schema/v2_0/MP_DispatcherHeader.asn1 new file mode 100644 index 00000000..103e8183 --- /dev/null +++ b/ASN.1 schema/v2_0/MP_DispatcherHeader.asn1 @@ -0,0 +1,13 @@ +MP_DispatcherHeaderModule + +DEFINITIONS +AUTOMATIC TAGS ::= +BEGIN +MP_DispatcherHeader ::= SEQUENCE +{ + dispatcherBodyEncoding INTEGER(0..2), + dispatcherMessageLength INTEGER(0..255), + protocolVersion INTEGER(0..255) +} + +END diff --git a/docs/examples/v1_1/501_513_response.per b/docs/examples/v1_1/501_513_response.per new file mode 100644 index 00000000..bc3d1535 --- /dev/null +++ b/docs/examples/v1_1/501_513_response.per @@ -0,0 +1 @@ +0963111008100D82E60C183060C183060C183060C183060C183060C183060C183060C183060C183062C183060C183062C1B3968D58346F93163C3898E36AD5A3772C70B7698F23864DD862C393235CAE3330CB918B46D85AB062C9376940000000000000040202468ACF1343530ECA864468ACF1342468ACF13420000085C0100A00000FC88F2A54DCCC3133C741B7082E3B79D3A5C6FEEC6CB6E7DE3D4A529B73E5DB6CFEF2A8B065919B165972B772D71E5C591B31C795AB06589AE370C5AE6CCDB2E36AD59B5CAD5B3261A0D1803FCCA72ABB768C1CB8976993372D5B335B54D8B15B1429A8E1D351C81694C81155842A54A952A5509CC60B260C5C9C86DEEOCADCDOC2CECADC4084D8EACBEAEEC3B72BAA9A796541C3965E7CFAF2CA836EFDDA7A6FE5A776741CFCF3E9976ACC7BF26575298B759DBOECEB95D3177BBOEDCAEA965CFD7661E4830E9E58B0E7E6B31EFC995D5160C1676C3B3AE574C5DEEC3B72BA8DCB7EEE8B79E5C3D1061D3CB161CFCD663DF932BAA2C18ACED87675CAE98BBDD876E575074F2C5873A0E7DF4F4C7A1663DF932BAA2C1CACED87675CAE983BDD876E5754FAEE414B7EFCCB31EFC995D5366D5676C3B3AE574C1DEEC3B72BAA5976EFE995063DFBBA72DFB1663DF932BAA6D98ACED87675CAE98BBDD876E575074F2418F7EEC9A7A69DFBB4EECEB31EFC995D5462C5676C3B3AE574C5DEEC3B72BA8BB32E3E9CB4E341437F7CBC9053E9972F2D3BB3ACC7BF265751685359DBOECEB95D3177BBOEDCAEA9E5C7D7969E9E5061D997974598F7E4CAEA9C16CD1676C3B3AE574C183060C183060C183060C183060C183160C183060C18B060C583162C183060C183060C183060C183060C183060C183060C183060C183160EF761DB95D42DFBB775E8829F4C3D3AF3598F7E4CAEA14F9D3AAD459DBOECEB95D3177BBOEDCAEA26FDFC9053E987A75E6B31EFC995D449F3E92CED87675CAE98B162C5DEEC3B72BA85BF7F4414FA61E9D79ACC7BF265750A7CFA8B3B61D9D72BA62EF761DB95D57D3BB26FEE829F4C3D3AF3598F7E4CAEABC99D127D759DBOECEB95D3060C183BDD876E575532EDE197961E9D7965598F7E4CAEAA459B422D28352AD28AB3B61D9D72BA6OEF761DB95D45DD9F4EECA829F4C3D3AF3598F7E4CAEA2CE8F2674559DBOECEB95D3177BBOEDCAEA965DBBFA6541074F2410F7EEE9CB7EC598F7E4CAEAA316361676C3B3AE574C5DEEC3B72BA8DCB7EEE8B70F8D995054D3CB2ACC7BF26575298B78CB3B61D9D72BA62E582346C9AB077BBOEDCAEA965C3C96E1F1B32A0A9A7965598F7E4CAEA5316F49676C3B3AE574C5CB0468D93560EF761DB95D4BCBE5050DFCF4F4D3BF72CC7BF265752E2D9A13E9ACED87675CAE98BBDD876E57517665C7D3969C682B65D1A71ECCAB31EFC995D45ACB3B61D9D72BA62EF761DB95D45AC821E8C3CB3E9DD9D04DDF932EC598F7E4CAEA2D69AB3B61D9D72BA62C5DEEC3B72BA9B1D057C3B3665E8B31EFC995D4D8F5D676C3B3AE574C5DEEC3B72BA8587A74CBCBCAOADBF674C39F2ACC7BF265750A0D4A91695959DBOECEB95D3176BF149950C18366CC183060C183966E1BB864C1C365CC983260BD8395EC9B2F6F9B1B27391B335AC99B86EB5A39C9996B8CACDAAD64D70B9CAD9930CCC98B54C1C3768C593060D1A0 \ No newline at end of file diff --git a/saic-java-api/pom.xml b/saic-java-api/pom.xml index a4d5b1bb..7330827f 100644 --- a/saic-java-api/pom.xml +++ b/saic-java-api/pom.xml @@ -128,6 +128,66 @@ + + generate-dispatcher-header-v2_0 + + java + + process-sources + + org.bn.compiler.Main + + -m + java + -o + ${project.build.directory}/generated-sources/asn1-java/net/heberling/ismart/asn1/v2_0 + -f + ${project.basedir}/../ASN.1 schema/v2_0/MP_DispatcherHeader.asn1 + -ns + net.heberling.ismart.asn1.v2_0 + + + + + generate-dispatcher-body-mp-v2_0 + + java + + process-sources + + org.bn.compiler.Main + + -m + java + -o + ${project.build.directory}/generated-sources/asn1-java/net/heberling/ismart/asn1/v2_0 + -f + ${project.basedir}/../ASN.1 schema/v2_0/MP_DispatcherBody.asn1 + -ns + net.heberling.ismart.asn1.v2_0 + + + + + generate-application-data-mp-v2_0 + + java + + process-sources + + org.bn.compiler.Main + + -m + java + -o + ${project.build.directory}/generated-sources/asn1-java/net/heberling/ismart/asn1/v2_0/entity + -f + ${project.basedir}/../ASN.1 schema/v2_1+0/ApplicationData.asn1 + -ns + net.heberling.ismart.asn1.v2_0.entity + + + generate-dispatcher-header-v2_1 diff --git a/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/Message.java b/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/Message.java new file mode 100644 index 00000000..45b5578a --- /dev/null +++ b/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/Message.java @@ -0,0 +1,19 @@ +package net.heberling.ismart.asn1.v2_0; + +import net.heberling.ismart.asn1.AbstractMessage; +import org.bn.coders.IASN1PreparedElement; + +public class Message + extends AbstractMessage { + + private final byte[] reserved; + + Message(MP_DispatcherHeader header, byte[] reserved, MP_DispatcherBody body, E applicationData) { + super(header, body, applicationData); + this.reserved = reserved; + } + + public byte[] getReserved() { + return reserved; + } +} diff --git a/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/MessageCoder.java b/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/MessageCoder.java new file mode 100644 index 00000000..e963a50d --- /dev/null +++ b/saic-java-api/src/main/java/net/heberling/ismart/asn1/v2_0/MessageCoder.java @@ -0,0 +1,135 @@ +package net.heberling.ismart.asn1.v2_0; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.time.Instant; +import net.heberling.ismart.asn1.AbstractMessageCoder; +import net.heberling.ismart.asn1.Util; +import org.bn.coders.IASN1PreparedElement; +import org.bn.coders.per.PERUnalignedDecoder; + +public class MessageCoder + extends AbstractMessageCoder> { + + public MessageCoder(Class applicationDataClass) { + super(applicationDataClass); + } + + @Override + public String encodeRequest(Message message) { + + E request = message.getApplicationData(); + + try { + + ByteArrayOutputStream bos; + byte[] applicationData; + MyPERUnalignedEncoder encoder = new MyPERUnalignedEncoder(); + if (request != null) { + bos = new ByteArrayOutputStream(); + encoder.encode(request, bos); + applicationData = bos.toByteArray(); + } else { + applicationData = new byte[0]; + } + + MP_DispatcherBody body = message.getBody(); + final DataEncodingType dataEncoding = new DataEncodingType(); + dataEncoding.setValue(DataEncodingType.EnumType.perUnaligned); + body.setApplicationDataEncoding(dataEncoding); + body.setApplicationDataLength(applicationData.length); + + bos = new ByteArrayOutputStream(); + encoder.encode(body, bos); + final byte[] bodyData = bos.toByteArray(); + + MP_DispatcherHeader header = message.getHeader(); + if (header.getProtocolVersion() == null) { + header.setProtocolVersion(33); + } + header.setDispatcherMessageLength(bodyData.length + 3 /*header length*/); + header.setDispatcherBodyEncoding(0); // PER + + bos = new ByteArrayOutputStream(); + bos.write(header.getProtocolVersion()); + bos.write(header.getDispatcherMessageLength()); + bos.write(header.getDispatcherBodyEncoding()); + + bos.write(message.getReserved()); + + bos.write(bodyData); + + bos.write(applicationData); + + byte[] bytes = bos.toByteArray(); + return "1" + String.format("%04X", bytes.length + 3) + bytesToHex(bytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Message decodeResponse(String message) { + try { + // TODO: check for message encoding and length + byte[] bytes = hexStringToByteArray(message.substring(5)); + InputStream inputStream = new ByteArrayInputStream(bytes); + // not asn.1 encoded + MP_DispatcherHeader header = new MP_DispatcherHeader(); + header.setProtocolVersion(inputStream.read()); + // messages can be longer than 256 bytes, so this value can be wrong! + header.setDispatcherMessageLength(inputStream.read()); + header.setDispatcherBodyEncoding(inputStream.read()); + + // TODO: whats this? + byte[] reserved = new byte[16]; + inputStream.read(reserved); + + final PERUnalignedDecoder decoder = new MyPERUnalignedDecoder(); + MP_DispatcherBody body = decoder.decode(inputStream, MP_DispatcherBody.class); + + E e = null; + if (getApplicationDataClass() != null && body.getApplicationDataLength() > 0) { + e = decoder.decode(inputStream, getApplicationDataClass()); + } + return new Message<>(header, reserved, body, e); + } catch (Exception e) { + throw new RuntimeException("Could not decode: " + message, e); + } + } + + @Override + public Message initializeMessage( + String uid, + String token, + String vin, + String applicationID, + int applicationDataProtocolVersion, + int messageID, + E applicationData) { + Message message = + new Message<>( + new MP_DispatcherHeader(), + Util.generateReservedBytes(), + new MP_DispatcherBody(), + applicationData); + + message.getBody().setMessageID(messageID); + message.getBody().setEventCreationTime((int) Instant.now().getEpochSecond()); + message.getBody().setApplicationID(applicationID); + message.getBody().setApplicationDataProtocolVersion(applicationDataProtocolVersion); + message.getBody().setTestFlag(2); + message.getBody().setUid(uid); + message.getBody().setToken(token); + message.getBody().setVin(vin); + message.getBody().setEventID(0); + + return message; + } + + @Override + public String getVersion() { + return "2.1"; + } +}