diff options
30 files changed, 400 insertions, 54 deletions
diff --git a/database/changelog-3.5.xml b/database/changelog-3.5.xml index f1af6ef10..3d1fa6b41 100644 --- a/database/changelog-3.5.xml +++ b/database/changelog-3.5.xml @@ -33,5 +33,22 @@ <column name="groupid" type="INT" /> </addColumn> + <dropDefaultValue tableName="users" columnName="map" /> + <dropDefaultValue tableName="users" columnName="language" /> + <dropDefaultValue tableName="users" columnName="distanceunit" /> + <dropDefaultValue tableName="users" columnName="speedunit" /> + + <addColumn tableName="users"> + <column name="twelvehourformat" type="BOOLEAN" defaultValueBoolean="false"> + <constraints nullable="false" /> + </column> + </addColumn> + + <addColumn tableName="server"> + <column name="twelvehourformat" type="BOOLEAN" defaultValueBoolean="false"> + <constraints nullable="false" /> + </column> + </addColumn> + </changeSet> </databaseChangeLog> @@ -67,7 +67,8 @@ speedUnit = :speedUnit, latitude = :latitude, longitude = :longitude, - zoom = :zoom + zoom = :zoom, + twelveHourFormat = :twelveHourFormat WHERE id = :id; </entry> @@ -101,7 +102,8 @@ speedUnit = :speedUnit, latitude = :latitude, longitude = :longitude, - zoom = :zoom + zoom = :zoom, + twelveHourFormat = :twelveHourFormat WHERE id = :id; </entry> @@ -298,5 +300,6 @@ <entry key='kenji.port'>5102</entry> <entry key='astra.port'>5103</entry> <entry key='homtecs.port'>5104</entry> + <entry key='fox.port'>5105</entry> </properties> diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/org/traccar/api/resource/GroupResource.java index 49f839499..e22796645 100644 --- a/src/org/traccar/api/resource/GroupResource.java +++ b/src/org/traccar/api/resource/GroupResource.java @@ -17,7 +17,6 @@ package org.traccar.api.resource; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.model.Device; import org.traccar.model.Group; import javax.ws.rs.Consumes; diff --git a/src/org/traccar/database/GroupTree.java b/src/org/traccar/database/GroupTree.java index b383b1501..4a2321f58 100644 --- a/src/org/traccar/database/GroupTree.java +++ b/src/org/traccar/database/GroupTree.java @@ -33,11 +33,11 @@ public class GroupTree { private Device device; private Collection<TreeNode> children = new HashSet<>(); - public TreeNode(Group group) { + TreeNode(Group group) { this.group = group; } - public TreeNode(Device device) { + TreeNode(Device device) { this.device = device; } @@ -59,14 +59,10 @@ public class GroupTree { if (other == this) { return true; } - if (group != null) { - if (other.group != null) { - return group.getId() == other.group.getId(); - } - } else if (device != null) { - if (other.device != null) { - return device.getId() == other.device.getId(); - } + if (group != null && other.group != null) { + return group.getId() == other.group.getId(); + } else if (device != null && other.device != null) { + return device.getId() == other.device.getId(); } return false; } diff --git a/src/org/traccar/geocode/BingMapsReverseGeocoder.java b/src/org/traccar/geocode/BingMapsReverseGeocoder.java index a5ebbd420..69148875a 100644 --- a/src/org/traccar/geocode/BingMapsReverseGeocoder.java +++ b/src/org/traccar/geocode/BingMapsReverseGeocoder.java @@ -20,10 +20,6 @@ import javax.json.JsonObject; public class BingMapsReverseGeocoder extends JsonReverseGeocoder { - public BingMapsReverseGeocoder() { - this("http://dev.virtualearth.net/REST/v1", "ABCDE", 0); - } - public BingMapsReverseGeocoder(String url, String key, int cacheSize) { super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize); } diff --git a/src/org/traccar/geocode/FactualReverseGeocoder.java b/src/org/traccar/geocode/FactualReverseGeocoder.java index 6e5a48423..15211f74a 100644 --- a/src/org/traccar/geocode/FactualReverseGeocoder.java +++ b/src/org/traccar/geocode/FactualReverseGeocoder.java @@ -19,10 +19,6 @@ import javax.json.JsonObject; public class FactualReverseGeocoder extends JsonReverseGeocoder { - public FactualReverseGeocoder() { - this("https://api.factual.com/geotag", "ABCDE", 0); - } - public FactualReverseGeocoder(String url, String key, int cacheSize) { super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize); } diff --git a/src/org/traccar/geocode/MapQuestReverseGeocoder.java b/src/org/traccar/geocode/MapQuestReverseGeocoder.java index be3f51b04..93edfdd09 100644 --- a/src/org/traccar/geocode/MapQuestReverseGeocoder.java +++ b/src/org/traccar/geocode/MapQuestReverseGeocoder.java @@ -20,10 +20,6 @@ import javax.json.JsonObject; public class MapQuestReverseGeocoder extends JsonReverseGeocoder { - public MapQuestReverseGeocoder() { - this("http://www.mapquestapi.com/geocoding/v1/reverse", "ABCDE", 0); - } - public MapQuestReverseGeocoder(String url, String key, int cacheSize) { super(url + "?key=" + key + "&location=%f,%f", cacheSize); } diff --git a/src/org/traccar/geocode/OpenCageReverseGeocoder.java b/src/org/traccar/geocode/OpenCageReverseGeocoder.java index 3104cb56a..b5b31179e 100644 --- a/src/org/traccar/geocode/OpenCageReverseGeocoder.java +++ b/src/org/traccar/geocode/OpenCageReverseGeocoder.java @@ -1,5 +1,6 @@ /* * Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com) + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +21,6 @@ import javax.json.JsonObject; public class OpenCageReverseGeocoder extends JsonReverseGeocoder { - public OpenCageReverseGeocoder() { - this("https://api.opencagedata.com/geocode/v1", "ABCDE", 0); - } - public OpenCageReverseGeocoder(String url, String key, int cacheSize) { super(url + "/json?q=%f,%f&key=" + key, cacheSize); } @@ -36,12 +33,21 @@ public class OpenCageReverseGeocoder extends JsonReverseGeocoder { if (location != null) { Address address = new Address(); + if (location.containsKey("building")) { + address.setHouse(location.getString("building")); + } if (location.containsKey("house_number")) { address.setHouse(location.getString("house_number")); } if (location.containsKey("road")) { address.setStreet(location.getString("road")); } + if (location.containsKey("suburb")) { + address.setSuburb(location.getString("suburb")); + } + if (location.containsKey("city")) { + address.setSettlement(location.getString("city")); + } if (location.containsKey("city_district")) { address.setSettlement(location.getString("city_district")); } diff --git a/src/org/traccar/model/Server.java b/src/org/traccar/model/Server.java index 00b1f60d0..f03a0aa60 100644 --- a/src/org/traccar/model/Server.java +++ b/src/org/traccar/model/Server.java @@ -137,4 +137,14 @@ public class Server { this.zoom = zoom; } + private boolean twelveHourFormat; + + public boolean getTwelveHourFormat() { + return twelveHourFormat; + } + + public void setTwelveHourFormat(boolean twelveHourFormat) { + this.twelveHourFormat = twelveHourFormat; + } + } diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java index 1531ba9e4..e58311834 100644 --- a/src/org/traccar/model/User.java +++ b/src/org/traccar/model/User.java @@ -140,6 +140,16 @@ public class User { this.zoom = zoom; } + private boolean twelveHourFormat; + + public boolean getTwelveHourFormat() { + return twelveHourFormat; + } + + public void setTwelveHourFormat(boolean twelveHourFormat) { + this.twelveHourFormat = twelveHourFormat; + } + private String password; public String getPassword() { diff --git a/src/org/traccar/protocol/FoxProtocol.java b/src/org/traccar/protocol/FoxProtocol.java new file mode 100644 index 000000000..cc069692b --- /dev/null +++ b/src/org/traccar/protocol/FoxProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.traccar.protocol; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.string.StringDecoder; +import org.traccar.BaseProtocol; +import org.traccar.CharacterDelimiterFrameDecoder; +import org.traccar.TrackerServer; + +import java.util.List; + +public class FoxProtocol extends BaseProtocol { + + public FoxProtocol() { + super("fox"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "</fox>")); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new FoxProtocolDecoder(FoxProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/FoxProtocolDecoder.java b/src/org/traccar/protocol/FoxProtocolDecoder.java new file mode 100644 index 000000000..b88da573f --- /dev/null +++ b/src/org/traccar/protocol/FoxProtocolDecoder.java @@ -0,0 +1,103 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.traccar.protocol; + +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class FoxProtocolDecoder extends BaseProtocolDecoder { + + public FoxProtocolDecoder(FoxProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .number("d+,") // status id + .expression("([AV]),") // validity + .number("(dd)(dd)(dd),") // date + .number("(dd)(dd)(dd),") // time + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.?d*)?,") // speed + .number("(d+.?d*)?,") // course + .any() + .compile(); + + private String getAttribute(String xml, String key) { + int start = xml.indexOf(key + "=\""); + if (start != -1) { + start += key.length() + 2; + int end = xml.indexOf("\"", start); + if (end != -1) { + return xml.substring(start, end); + } + } + return null; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String xml = (String) msg; + String id = getAttribute(xml, "id"); + String data = getAttribute(xml, "data"); + + if (id != null && data != null) { + + if (!identify(id, channel, remoteAddress)) { + return null; + } + + Parser parser = new Parser(PATTERN, data); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + position.setValid(parser.next().equals("A")); + + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java index 729c57573..e5762f5dd 100644 --- a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java +++ b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java @@ -21,8 +21,6 @@ import org.traccar.model.Command; public class MiniFinderProtocolEncoder extends StringProtocolEncoder { - private static final String prefix = "123456"; - @Override protected Object encodeCommand(Command command) { diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/org/traccar/protocol/TramigoProtocolDecoder.java index 10bab3d6c..1fd427ecf 100644 --- a/src/org/traccar/protocol/TramigoProtocolDecoder.java +++ b/src/org/traccar/protocol/TramigoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -130,6 +130,12 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { position.setTime(DateUtil.correctYear( dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); + if (sentence.contains("Ignition on detected")) { + position.set(Event.KEY_IGNITION, true); + } else if (sentence.contains("Ignition off detected")) { + position.set(Event.KEY_IGNITION, false); + } + return position; } diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java index 6b5d47023..0c35ef3a0 100644 --- a/src/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/org/traccar/protocol/WatchProtocolDecoder.java @@ -47,9 +47,9 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .number("(dd)(dd)(dd),") // date (ddmmyy) .number("(dd)(dd)(dd),") // time .expression("([AV]),") // validity - .number("-?(d+.d+),") // latitude + .number(" *-?(d+.d+),") // latitude .expression("([NS]),") - .number("-?(d+.d+),") // longitude + .number(" *-?(d+.d+),") // longitude .expression("([EW])?,") .number("(d+.d+),") // speed .number("(d+.?d*),") // course diff --git a/test/org/traccar/geocode/ReverseGeocoderTest.java b/test/org/traccar/geocode/ReverseGeocoderTest.java index a572b0456..ada0afbbe 100644 --- a/test/org/traccar/geocode/ReverseGeocoderTest.java +++ b/test/org/traccar/geocode/ReverseGeocoderTest.java @@ -8,45 +8,75 @@ public class ReverseGeocoderTest { private boolean enable = false; @Test - public void test() { + public void test() throws InterruptedException { if (enable) { testGoogle(); - testNominatim(); - testGisgraphy(); } } - public void testGoogle() { + private String address; + + private synchronized String waitAddress() { + try { + wait(5000); + return address; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private synchronized void setAddress(String address) { + this.address = address; + notifyAll(); + } + + public void testGoogle() throws InterruptedException { ReverseGeocoder reverseGeocoder = new GoogleReverseGeocoder(); reverseGeocoder.getAddress(new AddressFormat(), 37.4217550, -122.0846330, new ReverseGeocoder.ReverseGeocoderCallback() { @Override public void onResult(String address) { - Assert.assertEquals("1600 Amphitheatre Pkwy, Mountain View, CA, US", address); + setAddress(address); } }); + Assert.assertEquals("1600 Amphitheatre Pkwy, Mountain View, CA, US", waitAddress()); } - public void testNominatim() { + public void testNominatim() throws InterruptedException { ReverseGeocoder reverseGeocoder = new NominatimReverseGeocoder(); reverseGeocoder.getAddress(new AddressFormat(), 40.7337807, -73.9974401, new ReverseGeocoder.ReverseGeocoderCallback() { @Override public void onResult(String address) { - Assert.assertEquals("35 West 9th Street, NYC, New York, US", address); + setAddress(address); } }); + Assert.assertEquals("35 West 9th Street, NYC, New York, US", waitAddress()); } - public void testGisgraphy() { + public void testGisgraphy() throws InterruptedException { ReverseGeocoder reverseGeocoder = new GisgraphyReverseGeocoder(); reverseGeocoder.getAddress(new AddressFormat(), 48.8530000, 2.3400000, new ReverseGeocoder.ReverseGeocoderCallback() { @Override public void onResult(String address) { - Assert.assertEquals("Rue du Jardinet, Paris, FR", address); + setAddress(address); + } + }); + Assert.assertEquals("Rue du Jardinet, Paris, FR", waitAddress()); + } + + public void testOpenCage() throws InterruptedException { + ReverseGeocoder reverseGeocoder = new OpenCageReverseGeocoder( + "http://api.opencagedata.com/geocode/v1", "SECRET", 0); + + reverseGeocoder.getAddress(new AddressFormat(), 34.116302, -118.051519, new ReverseGeocoder.ReverseGeocoderCallback() { + @Override + public void onResult(String address) { + setAddress(address); } }); + Assert.assertEquals("Charleston Road, California, US", waitAddress()); } } diff --git a/test/org/traccar/protocol/FoxProtocolDecoderTest.java b/test/org/traccar/protocol/FoxProtocolDecoderTest.java new file mode 100644 index 000000000..304db1891 --- /dev/null +++ b/test/org/traccar/protocol/FoxProtocolDecoderTest.java @@ -0,0 +1,27 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class FoxProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + FoxProtocolDecoder decoder = new FoxProtocolDecoder(new FoxProtocol()); + + verifyPosition(decoder, text( + "<fox><gps id=\"90\" data=\"1092,V,010101,000004,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664\"/></fox>")); + + verifyPosition(decoder, text( + "<fox><gps id=\"90\" data=\"31,V,110316,125952,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,65 60 60 60 60 60 60 60 65 65 55 55 60 60 60 60 60 60 60 60 55 55 60 55 65 60 60 60 60 60 60 55\"/></fox>")); + + verifyPosition(decoder, text( + "<fox><gps id=\"90\" data=\"0,V,110316,130154,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664 0\"/></fox>")); + + verifyPosition(decoder, text( + "<fox><gps id=\"90\" data=\"0,A,110316,131834,4448.8355,N,02028.2021,E,0,217,,1111111111111111 123 0 0 0 0 0 00000000 50020,50020 0\"/></fox>")); + + } + +} diff --git a/test/org/traccar/protocol/TramigoProtocolDecoderTest.java b/test/org/traccar/protocol/TramigoProtocolDecoderTest.java index 9ac02a7ad..a11e69a6a 100644 --- a/test/org/traccar/protocol/TramigoProtocolDecoderTest.java +++ b/test/org/traccar/protocol/TramigoProtocolDecoderTest.java @@ -11,7 +11,28 @@ public class TramigoProtocolDecoderTest extends ProtocolTest { public void testDecode() throws Exception { TramigoProtocolDecoder decoder = new TramigoProtocolDecoder(new TramigoProtocol()); - + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "8000853eb000b8000101fcff032f14665a89e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3135206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736352c20332e33343735352c2031303a3031204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "8000973eb000b90001012128032f14667794e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c2073746f707065642c20302e3134206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736372c20332e33343737332c2031303a3438204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "8000b73eb000ad000101fdd2032f1466c9cbe2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3131206b6d2045206f6620416c68616a69204d6173686120526f616420466f6f746272696467652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303031342c20332e33353434332c2031343a3434204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "8000883eb000d3000101b223032f1466fc89e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30323a34312c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639332c20332e33343636302c2031303a3033204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "80009a3eb000d300010109ff032f1466b195e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30353a31342c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639312c20332e33343636322c2031303a3533204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "8000bc3eb000ba000101622c032f1466bacce2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30343a30302c206d6f76696e672c20617420416b6572656c6520526f61642d4f67756e6c616e612044726976652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303630332c20332e33353232382c2031343a3438204d6172203131202020454f46")); + + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, + "80001d3cb000b3000101160f032f1466b475e0564176656e7369732053797353657276653a205374617475732c204750533a203931252c2047534d3a203737252c20475052533a20436f6e6e65637465642c20626174746572793a20313030252c207265706f7274733a2049676e6974696f6e20286f6666292c205374617475732028352c322e302c3732302c3330292c20362e34393239382c20332e33343836352c2031393a3038204d6172203920454f46")); + verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN, "80005408b000af000101b23903677f00c8436d3842616c697365204f6e653a20416c6c756d616765206d61726368652064e974656374e92c20676172e92c20302e3735206b6d20452064652045636f6c65204175746f726f757465206465204b696e73686173612c2056696c6c65206465204b696e73686173612c204b696e73686173612c2043442c202d342e33343130362c2031352e33343931352c2030313a3030204a616e2031202020454f46")); diff --git a/test/org/traccar/protocol/WatchProtocolDecoderTest.java b/test/org/traccar/protocol/WatchProtocolDecoderTest.java index b7320098f..89e9a7e77 100644 --- a/test/org/traccar/protocol/WatchProtocolDecoderTest.java +++ b/test/org/traccar/protocol/WatchProtocolDecoderTest.java @@ -10,6 +10,12 @@ public class WatchProtocolDecoderTest extends ProtocolTest { WatchProtocolDecoder decoder = new WatchProtocolDecoder(new WatchProtocol()); + verifyPosition(decoder, text( + "[3G*4700222306*0077*UD,120316,140610,V,48.779045,N, 9.1574736,E,0.00,0.0,0.0,0,25,83,0,0,00000000,2,255,262,1,21041,9067,121,21041,5981,116")); + + verifyPosition(decoder, text( + "[3G*4700222306*011F*UD2,120316,140444,A,48.779045,N, 9.1574736,E,0.57,12.8,0.0,7,28,77,0,0,00000000,2,2,262,1,21041,9067,121,21041,5981,116,5,WG-Superlativ,34:31:c4:c8:a9:22,-67,EasyBox-28E858,18:83:bf:28:e8:f4,-70,MoMaXXg,be:05:43:b7:19:15,-72,MoMaXX2,bc:05:43:b7:19:15,-72,Gastzugang,18:83:bf:28:e8:f5,-72")); + verifyNothing(decoder, text( "[SG*9081000548*0009*LK,0,100")); diff --git a/web/app/AttributeFormatter.js b/web/app/AttributeFormatter.js index 1cbc1cfc0..3432ca1e0 100644 --- a/web/app/AttributeFormatter.js +++ b/web/app/AttributeFormatter.js @@ -40,7 +40,11 @@ Ext.define('Traccar.AttributeFormatter', { } else if (typeof value === 'boolean') { return value ? Ext.Msg.buttonText.yes : Ext.Msg.buttonText.no; } else if (value instanceof Date) { - return Ext.Date.format(value, Traccar.Style.dateTimeFormat); + if (Traccar.app.getPreference('twelveHourFormat', false)) { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat12); + } else { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat24); + } } return value; }, diff --git a/web/app/Style.js b/web/app/Style.js index f4868aec3..309a3937e 100644 --- a/web/app/Style.js +++ b/web/app/Style.js @@ -22,8 +22,10 @@ Ext.define('Traccar.Style', { windowWidth: 640, windowHeight: 480, - dateTimeFormat: 'Y-m-d H:i:s', - timeFormat: 'H:i', + dateTimeFormat24: 'Y-m-d H:i:s', + dateTimeFormat12: 'Y-m-d g:i:s a', + timeFormat24: 'H:i', + timeFormat12: 'g:i a', dateFormat: 'Y-m-d', weekStartDay: 1, diff --git a/web/app/model/Server.js b/web/app/model/Server.js index 9b4b5b2b6..796ff58d6 100644 --- a/web/app/model/Server.js +++ b/web/app/model/Server.js @@ -54,6 +54,9 @@ Ext.define('Traccar.model.Server', { }, { name: 'zoom', type: 'int' + }, { + name: 'twelveHourFormat', + type: 'boolean' }], proxy: { diff --git a/web/app/model/User.js b/web/app/model/User.js index b00918bc8..237a4d269 100644 --- a/web/app/model/User.js +++ b/web/app/model/User.js @@ -54,6 +54,9 @@ Ext.define('Traccar.model.User', { }, { name: 'zoom', type: 'int' + }, { + name: 'twelveHourFormat', + type: 'boolean' }], proxy: { diff --git a/web/app/view/CustomTimeField.js b/web/app/view/CustomTimeField.js new file mode 100644 index 000000000..1bd8c7307 --- /dev/null +++ b/web/app/view/CustomTimeField.js @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.view.CustomTimeField', { + extend: 'Ext.form.field.Time', + xtype: 'customTimeField', + + constructor: function (config) { + if (Traccar.app.getPreference('twelveHourFormat', false)) { + config.format = Traccar.Style.timeFormat12; + } else { + config.format = Traccar.Style.timeFormat24; + } + this.callParent(arguments); + } +}); diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index ebe3ca195..b464ed561 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -62,6 +62,31 @@ Ext.define('Traccar.view.Devices', { }] }, + bbar: [{ + xtype: 'tbtext', + html: Strings.sharedSearch + }, { + xtype: 'textfield', + listeners: { + change: function () { + var tree, expr; + tree = this.up('treepanel'); + expr = new RegExp(this.getValue(), 'i'); + tree.store.filter({ + id: 'nameFilter', + filterFn: function (node) { + var children, len, visible, i; + children = node.childNodes; + len = children && children.length; + visible = node.isLeaf() ? expr.test(node.get('name')) : false; + for (i = 0; i < len && !(visible = children[i].get('visible')); i++); + return visible; + } + }); + } + } + }], + listeners: { selectionchange: 'onSelectionChange', beforeselect: 'onBeforeSelect' @@ -89,7 +114,11 @@ Ext.define('Traccar.view.Devices', { metaData.tdCls = 'status-color-unknown'; break; } - return Ext.Date.format(value, Traccar.Style.dateTimeFormat); + if (Traccar.app.getPreference('twelveHourFormat', false)) { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat12); + } else { + return Ext.Date.format(value, Traccar.Style.dateTimeFormat24); + } } } }] diff --git a/web/app/view/MapController.js b/web/app/view/MapController.js index 918f81390..b8b7290b8 100644 --- a/web/app/view/MapController.js +++ b/web/app/view/MapController.js @@ -170,7 +170,7 @@ Ext.define('Traccar.view.MapController', { style = this.getReportMarker(); style.getImage().setRotation(position.get('course') * Math.PI / 180); /*style.getText().setText( - Ext.Date.format(position.get('fixTime'), Traccar.Style.dateTimeFormat));*/ + Ext.Date.format(position.get('fixTime'), Traccar.Style.dateTimeFormat24));*/ marker.setStyle(style); diff --git a/web/app/view/Report.js b/web/app/view/Report.js index 91ccba917..4261b9040 100644 --- a/web/app/view/Report.js +++ b/web/app/view/Report.js @@ -19,7 +19,8 @@ Ext.define('Traccar.view.Report', { xtype: 'reportView', requires: [ - 'Traccar.view.ReportController' + 'Traccar.view.ReportController', + 'Traccar.view.CustomTimeField' ], controller: 'report', @@ -48,10 +49,9 @@ Ext.define('Traccar.view.Report', { format: Traccar.Style.dateFormat, value: new Date(new Date().getTime() - 30 * 60 * 1000) }, { - xtype: 'timefield', + xtype: 'customTimeField', reference: 'fromTimeField', maxWidth: Traccar.Style.reportTime, - format: Traccar.Style.timeFormat, value: new Date(new Date().getTime() - 30 * 60 * 1000) }, '-', { xtype: 'tbtext', @@ -63,10 +63,9 @@ Ext.define('Traccar.view.Report', { format: Traccar.Style.dateFormat, value: new Date() }, { - xtype: 'timefield', + xtype: 'customTimeField', reference: 'toTimeField', maxWidth: Traccar.Style.reportTime, - format: Traccar.Style.timeFormat, value: new Date() }, '-', { text: Strings.reportShow, diff --git a/web/app/view/ServerDialog.js b/web/app/view/ServerDialog.js index 9fbbed920..67f3a7ab2 100644 --- a/web/app/view/ServerDialog.js +++ b/web/app/view/ServerDialog.js @@ -79,6 +79,11 @@ Ext.define('Traccar.view.ServerDialog', { xtype: 'numberfield', name: 'zoom', fieldLabel: Strings.serverZoom + }, { + xtype: 'checkboxfield', + name: 'twelveHourFormat', + fieldLabel: Strings.settingsTwelveHourFormat, + allowBlank: false }] } }); diff --git a/web/app/view/UserDialog.js b/web/app/view/UserDialog.js index c1ed2fece..d0e8bda52 100644 --- a/web/app/view/UserDialog.js +++ b/web/app/view/UserDialog.js @@ -84,6 +84,11 @@ Ext.define('Traccar.view.UserDialog', { xtype: 'numberfield', name: 'zoom', fieldLabel: Strings.serverZoom + }, { + xtype: 'checkboxfield', + name: 'twelveHourFormat', + fieldLabel: Strings.settingsTwelveHourFormat, + allowBlank: false }] } }); diff --git a/web/l10n/en.json b/web/l10n/en.json index cabd76362..4fb72c670 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -14,6 +14,7 @@ "sharedMinute": "Minute", "sharedSecond": "Second", "sharedName": "Name", + "sharedSearch": "Search", "errorTitle": "Error", "errorUnknown": "Unknown error", "errorConnection": "Connection error", @@ -43,6 +44,7 @@ "settingsUsers": "Users", "settingsDistanceUnit": "Distance", "settingsSpeedUnit": "Speed", + "settingsTwelveHourFormat": "12-hour Format", "reportTitle": "Reports", "reportDevice": "Device", "reportFrom": "From", |