DELL
2024-01-13 4a6db1d471d81415d0c407cb1d91ed926593b4e3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * 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();
    }
 
}