/*
|
* Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*
|
*/
|
package org.openmuc.openiec61850;
|
|
import java.util.Calendar;
|
import java.util.Date;
|
|
import org.openmuc.jasn1.ber.types.BerNull;
|
import org.openmuc.openiec61850.internal.mms.asn1.Data;
|
import org.openmuc.openiec61850.internal.mms.asn1.TypeDescription;
|
import org.openmuc.openiec61850.internal.mms.asn1.UtcTime;
|
|
public final class BdaTimestamp extends BasicDataAttribute {
|
|
private byte[] value;
|
|
public BdaTimestamp(ObjectReference objectReference, Fc fc, String sAddr, boolean dchg, boolean dupd) {
|
super(objectReference, fc, sAddr, dchg, dupd);
|
basicType = BdaType.TIMESTAMP;
|
setDefault();
|
}
|
|
/**
|
* The SecondSinceEpoch shall be the interval in seconds continuously counted from the epoch 1970-01-01 00:00:00 UTC
|
*/
|
|
/**
|
* Returns the value as the number of seconds since epoch 1970-01-01 00:00:00 UTC
|
*
|
* @return the number of seconds since epoch 1970-01-01 00:00:00 UTC
|
*/
|
private long getSecondsSinceEpoch() {
|
return ((0xffL & value[0]) << 24 | (0xffL & value[1]) << 16 | (0xffL & value[2]) << 8 | (0xffL & value[3]));
|
}
|
|
/**
|
* The attribute FractionOfSecond shall be the fraction of the current second when the value of the TimeStamp has
|
* been determined. The fraction of second shall be calculated as
|
* <code>(SUM from I = 0 to 23 of bi*2**–(I+1) s).</code>
|
*
|
* NOTE 1 The resolution is the smallest unit by which the time stamp is updated. The 24 bits of the integer
|
* provides 1 out of 16777216 counts as the smallest unit; calculated by 1/2**24 which equals approximately 60 ns.
|
*
|
* NOTE 2 The resolution of a time stamp may be 1/2**1 (= 0,5 s) if only the first bit is used; or may be 1/2**2 (=
|
* 0,25 s) if the first two bits are used; or may be approximately 60 ns if all 24 bits are used. The resolution
|
* provided by an IED is outside the scope of this standard.
|
*
|
* @return the fraction of seconds
|
*/
|
private int getFractionOfSecond() {
|
return ((0xff & value[4]) << 16 | (0xff & value[5]) << 8 | (0xff & value[6]));
|
}
|
|
public void setDate(Date date) {
|
if (value == null) {
|
value = new byte[8];
|
}
|
|
int secondsSinceEpoch = (int) (date.getTime() / 1000L);
|
int fractionOfSecond = (int) ((date.getTime() % 1000L) / 1000.0 * (1 << 24));
|
|
// 0x8a = time accuracy of 10 and LeapSecondsKnown = true, ClockFailure
|
// = false, ClockNotSynchronized = false
|
value = new byte[] { (byte) ((secondsSinceEpoch >> 24) & 0xff), (byte) ((secondsSinceEpoch >> 16) & 0xff),
|
(byte) ((secondsSinceEpoch >> 8) & 0xff), (byte) (secondsSinceEpoch & 0xff),
|
(byte) ((fractionOfSecond >> 16) & 0xff), (byte) ((fractionOfSecond >> 8) & 0xff),
|
(byte) (fractionOfSecond & 0xff), (byte) 0x8a };
|
|
}
|
|
public void setDate(Date date, boolean leapSecondsKnown, boolean clockFailure, boolean clockNotSynchronized,
|
int timeAccuracy) {
|
if (value == null) {
|
value = new byte[8];
|
}
|
|
int secondsSinceEpoch = (int) (date.getTime() / 1000L);
|
int fractionOfSecond = (int) ((date.getTime() % 1000L) / 1000.0 * (1 << 24));
|
|
int timeQuality = timeAccuracy & 0x1f;
|
if (leapSecondsKnown) {
|
timeQuality = timeQuality | 0x80;
|
}
|
if (clockFailure) {
|
timeQuality = timeQuality | 0x40;
|
}
|
if (clockNotSynchronized) {
|
timeQuality = timeQuality | 0x20;
|
}
|
|
value = new byte[] { (byte) ((secondsSinceEpoch >> 24) & 0xff), (byte) ((secondsSinceEpoch >> 16) & 0xff),
|
(byte) ((secondsSinceEpoch >> 8) & 0xff), (byte) (secondsSinceEpoch & 0xff),
|
(byte) ((fractionOfSecond >> 16) & 0xff), (byte) ((fractionOfSecond >> 8) & 0xff),
|
(byte) (fractionOfSecond & 0xff), (byte) timeQuality };
|
|
}
|
|
public void setValue(byte[] value) {
|
if (value == null) {
|
this.value = new byte[8];
|
}
|
this.value = value;
|
}
|
|
@Override
|
void setValueFrom(BasicDataAttribute bda) {
|
byte[] srcValue = ((BdaTimestamp) bda).getValue();
|
if (value.length != srcValue.length) {
|
value = new byte[srcValue.length];
|
}
|
System.arraycopy(srcValue, 0, value, 0, srcValue.length);
|
}
|
|
public Date getDate() {
|
if (value == null || value.length == 0) {
|
return null;
|
}
|
long time = getSecondsSinceEpoch() * 1000L + (long) (((float) getFractionOfSecond()) / (1 << 24) * 1000 + 0.5);
|
return new Date(time);
|
}
|
|
public byte[] getValue() {
|
return value;
|
}
|
|
/**
|
* The value TRUE of the attribute LeapSecondsKnown shall indicate that the value for SecondSinceEpoch takes into
|
* account all leap seconds occurred. If it is FALSE then the value does not take into account the leap seconds that
|
* occurred before the initialization of the time source of the device.
|
*
|
* Java {@link Date} and {@link Calendar} objects do handle leap seconds, so this is usually true.
|
*
|
* @return TRUE of the attribute LeapSecondsKnown shall indicate that the value for SecondSinceEpoch takes into
|
* account all leap seconds occurred
|
*/
|
public boolean getLeapSecondsKnown() {
|
return ((value[7] & 0x80) != 0);
|
}
|
|
/**
|
* The attribute clockFailure shall indicate that the time source of the sending device is unreliable. The value of
|
* the TimeStamp shall be ignored.
|
*
|
* @return true if the time source of the sending device is unreliable
|
*/
|
public boolean getClockFailure() {
|
return ((value[7] & 0x40) != 0);
|
}
|
|
/**
|
* The attribute clockNotSynchronized shall indicate that the time source of the sending device is not synchronized
|
* with the external UTC time.
|
*
|
* @return true if the time source of the sending device is not synchronized
|
*/
|
public boolean getClockNotSynchronized() {
|
return ((value[7] & 0x20) != 0);
|
}
|
|
/**
|
* The attribute TimeAccuracy shall represent the time accuracy class of the time source of the sending device
|
* relative to the external UTC time. The timeAccuracy classes shall represent the number of significant bits in the
|
* FractionOfSecond
|
*
|
* If the time is set via Java {@link Date} objects, the accuracy is 1 ms, that is a timeAccuracy value of 10.
|
*
|
* @return the time accuracy
|
*/
|
public int getTimeAccuracy() {
|
return ((value[7] & 0x1f));
|
}
|
|
/**
|
* Sets Timestamp the empty byte array (indicating an invalid Timestamp)
|
*/
|
@Override
|
public void setDefault() {
|
value = new byte[8];
|
}
|
|
/**
|
* Sets Timestamp to current time
|
*/
|
public void setCurrentTime() {
|
setDate(new Date());
|
}
|
|
@Override
|
public BdaTimestamp copy() {
|
BdaTimestamp copy = new BdaTimestamp(objectReference, fc, sAddr, dchg, dupd);
|
byte[] valueCopy = new byte[value.length];
|
System.arraycopy(value, 0, valueCopy, 0, value.length);
|
copy.setValue(valueCopy);
|
if (mirror == null) {
|
copy.mirror = this;
|
}
|
else {
|
copy.mirror = mirror;
|
}
|
return copy;
|
}
|
|
@Override
|
Data getMmsDataObj() {
|
Data data = new Data();
|
data.setUtcTime(new UtcTime(value));
|
return data;
|
}
|
|
@Override
|
void setValueFromMmsDataObj(Data data) throws ServiceError {
|
if (data.getUtcTime() == null) {
|
throw new ServiceError(ServiceError.TYPE_CONFLICT, "expected type: utc_time/timestamp");
|
}
|
value = data.getUtcTime().value;
|
}
|
|
@Override
|
TypeDescription getMmsTypeSpec() {
|
TypeDescription typeDescription = new TypeDescription();
|
typeDescription.setUtcTime(new BerNull());
|
return typeDescription;
|
}
|
|
@Override
|
public String toString() {
|
return getReference().toString() + ": " + getDate();
|
}
|
|
}
|