In order for the users to implement their own features and ideas, we now provide a "user-exit" mechanism.
The user-exits are to be written in Java, and implement a specific interface named
olivsoftdesktop.DesktopUserExitInterface,
and defined as foillow:
1 package olivsoftdesktop;
2
3 public interface DesktopUserExitInterface
4 {
5 public void start();
6 public void stop();
7 public void describe();
8 }
A Simple User-exit implementation
To develop your own features, you would need to put - at least - into your classpath:desktop.jar
nmeaparser.jarnmeareader.jarcoreutilitiesgeomutil.jar
Here is a simple implementation of this interface. This one evaluates the True Wind Speed every time a sentence is received from the NMEA station, and displays a message if the TWS is above 10 knots.
See how the
NMEAReaderListener is registered.
1 package olivsoftdesktop.sampleue;
2
3 import nmea.event.NMEAReaderListener;
4 import nmea.server.ctx.NMEAContext;
5 import nmea.server.ctx.NMEADataCache;
6 import ocss.nmea.parser.Angle360;
7 import ocss.nmea.parser.Speed;
8 import olivsoftdesktop.DesktopUserExitInterface;
9
10 public class UserExitSample
11 implements DesktopUserExitInterface
12 {
13 public UserExitSample()
14 {
15 super();
16 }
17
18 @Override
19 public void start()
20 {
21 System.out.println("User exit is starting...");
22 NMEAContext.getInstance().addNMEAReaderListener(new NMEAReaderListener()
23 {
24 @Override
25 public void manageNMEAString(String nmeaString)
26 {
27 // System.out.println(" ... From user exit, got NMEA Data [" + nmeaString + "]");
28 NMEADataCache dc = NMEAContext.getInstance().getCache();
29 double tws = ((Speed) dc.get(NMEADataCache.TWS)).getValue();
30 double twd = ((Angle360) dc.get(NMEADataCache.TWD)).getValue();
31 if (tws > 10 && !Double.isInfinite(tws))
32 {
33 System.out.println("Wind is over 10 kts:" + tws + ", TWD:" + twd);
34 // TODO Send an email...
35 }
36 }
37 });
38 }
39
40 @Override
41 public void stop()
42 {
43 System.out.println("Terminating User exit");
44 }
45
46 @Override
47 public void describe()
48 {
49 System.out.println("This is a simple user-exit example that shows howto register an NMEAReaderListener from your code.");
50 }
51 }
User-exit runtime
To have your user-exit to be taken care if, you need to:-
Archive it in a jar-file, and put the jar (along with the ones it may depend on) in one of the following directories
all-user-exits(recommended)all-libsall-3rd-party
-
Mention the name of the user-exit in the command-line parameters, like
-ue:myspecial.feature.SuperUserExit. For the example above, the parameter would be-ue:olivsoftdesktop.sampleue.UserExitSample.
A more complex sample
Here is the scenario:You have an internet connection on the boat, it is docked or anchored in the harbor.
From wherever you are, you want to know what the wind is like where the boat is.
This user-exit monitors the True Wind Speed (TWS), and send an email when it is above a given threshold. It looks at the wind speed every X minutes, the X comes from a configuration file (
email.properties) that can be edited.
1 package olivsoftdesktopuserexits;
2
3 import java.io.FileInputStream;
4 import java.text.DecimalFormat;
5 import java.text.SimpleDateFormat;
6 import java.util.Calendar;
7 import java.util.Date;
8 import java.util.Properties;
9 import java.util.TimeZone;
10 import nmea.server.ctx.NMEAContext;
11 import nmea.server.ctx.NMEADataCache;
12 import ocss.nmea.parser.Angle360;
13 import ocss.nmea.parser.GeoPos;
14 import ocss.nmea.parser.Speed;
15 import ocss.nmea.parser.UTCDate;
16 import olivsoftdesktop.DesktopUserExitInterface;
17 import olivsoftdesktopuserexits.emailutil.EmailSender;
18
19 public class DesktopEmailSender
20 implements DesktopUserExitInterface
21 {
22 private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
23 private final static DecimalFormat DF22 = new DecimalFormat("##0.00 'kts'");
24 private final static DecimalFormat DF30 = new DecimalFormat("##0'\272'");
25 private static String SEND_PROVIDER = "google";
26 private Thread watcher = null;
27 private boolean keepWatching = true;
28 private EmailSender sender = null;
29
30 private double windThreshold = -1;
31 private long betweenLoops = 600 * 1000L; // 10 minutes default
32
33 public DesktopEmailSender()
34 {
35 super();
36 }
37
38 @Override
39 public void start()
40 {
41 System.out.println("Method 'start':" + this.getClass().getName() + " User exit is starting...");
42 Properties props = new Properties();
43 String propFile = "email.properties";
44 try
45 {
46 FileInputStream fis = new FileInputStream(propFile);
47 props.load(fis);
48 }
49 catch (Exception e)
50 {
51 System.err.println("email.properies file problem..., from " + System.getProperty("user.dir"));
52 throw new RuntimeException("File not found:email.properies");
53 }
54 SEND_PROVIDER = props.getProperty("ue.preferred.provider", SEND_PROVIDER);
55 sender = new EmailSender(SEND_PROVIDER);
56 try
57 {
58 windThreshold = Double.parseDouble(props.getProperty("ue.wind.threshold"));
59 System.out.println("Will send emails when the wind is above [" + windThreshold + "]");
60 }
61 catch (NumberFormatException nfe)
62 {
63 throw new RuntimeException("Bad wind threshold:" + props.getProperty("ue.wind.threshold"));
64 }
65 try
66 {
67 betweenLoops = Long.parseLong(props.getProperty("ue.between.loops.in.minute"));
68 }
69 catch (NumberFormatException nfe)
70 {
71 throw new RuntimeException("Bad Loop interval:" + props.getProperty("ue.between.loops.in.minute"));
72 }
73 final long _betweenLoops = betweenLoops;
74 watcher = new Thread()
75 {
76 private boolean started = false;
77 private final long BETWEEN_LOOPS = _betweenLoops * 60 * 1000;
78 private final long TEN_SECONDS = 10000L;
79 private long waitTime = BETWEEN_LOOPS;
80 public void run()
81 {
82 while (keepWatching)
83 {
84 waitTime = BETWEEN_LOOPS;
85 NMEADataCache dc = NMEAContext.getInstance().getCache();
86 try
87 {
88 double tws = ((Speed) dc.get(NMEADataCache.TWS)).getValue();
89 double twd = ((Angle360) dc.get(NMEADataCache.TWD)).getValue();
90 String date = "";
91 UTCDate utcDate = (UTCDate)NMEAContext.getInstance().getCache().get(NMEADataCache.GPS_DATE_TIME);
92 if (utcDate != null && utcDate.getValue() != null)
93 {
94 Date d = utcDate.getValue();
95 Calendar cal = Calendar.getInstance();
96 cal.setTime(d);
97 cal.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
98 date = SDF.format(cal.getTime());
99 }
100 String pos = "";
101 try { pos = ((GeoPos)dc.get(NMEADataCache.POSITION)).toString(); } catch (Exception ex) {}
102 if (!started)
103 {
104 started = true;
105 System.out.println(" -- User exit started for good.");
106 }
107 if (tws > windThreshold && !Double.isInfinite(tws))
108 {
109 String alertMessage =
110 (date.trim().length() > 0 ? "Date:" + date + "\n": "") +
111 (pos.trim().length() > 0 ? "Pos:" + pos + "\n" : "") +
112 "Wind is over " + DF22.format(windThreshold) + ":" + DF22.format(tws) + ", TWD:" + DF30.format(twd);
113 System.out.println(alertMessage);
114 // Send an email...
115 try
116 {
117 sender.send(alertMessage);
118 System.out.println("Email sent.");
119 }
120 catch (Exception ex)
121 {
122 System.err.println("Sending email failed through [" + SEND_PROVIDER + "]");
123 ex.printStackTrace();
124 }
125 }
126 }
127 catch (NullPointerException npe)
128 {
129 // Just wait til next time...
130 System.out.println("Cache not initialized (yet)");
131 waitTime = TEN_SECONDS;
132 }
133 synchronized (this)
134 {
135 System.out.println(" ...User exit going to wait, at " + new Date().toString() + " (will wait for " + (waitTime / 1000) + " s)");
136 try { wait(waitTime); }
137 catch (InterruptedException ie)
138 {
139 System.out.println("Told to stop!");
140 keepWatching = false;
141 }
142 }
143 }
144 System.out.println("Stop waiting.");
145 }
146 };
147 keepWatching = true;
148 watcher.start();
149 }
150
151 @Override
152 public void stop()
153 {
154 System.out.println(this.getClass().getName() + " is terminating");
155 keepWatching = false;
156 synchronized (watcher)
157 {
158 watcher.notify();
159 }
160 }
161
162 @Override
163 public void describe()
164 {
165 System.out.println("Polls the NMEA Cache on a regular base, and sends an email if the TWS is above a given threshold.");
166 System.out.println("Driven by a properties file named email.properties, in the all-scripts directory.");
167 }
168 }
Possibilities are endless. The limit is your imagination.
Combining the two examples above, you can as well gather all the data into a single document (XML, json, etc), and send it through email on a regular base, so it can be rendered by the receipient.
Etc, etc...
How to do it for yourself, step by step
- Download all the sources, in the zip mentionned above
- Extract it is a new clean directory
- Make sure the jar
mail.jaris in yourall-3rd-partydirectory - Make sure you have installed a JDK in your environment
- In a system console, navigate to the directory where you unzipped the sources
- If it does not exist, create a
classesdirectory. Make sure it is empty -
Compile the code:
On WindowsOn LinuxPrompt> set OLIV_HOME=D:\OlivSoft Prompt> set CP=%OLIV_HOME%\all-3rd-party\mail.jar Prompt> set CP=%CP%;%OLIV_HOME%\all-libs\nmeaparser.jar Prompt> set CP=%CP%;%OLIV_HOME%\all-libs\nmeareader.jar Prompt> set CP=%CP%;%OLIV_HOME%\all-libs\desktop.jar Prompt> set CP=%CP%;%OLIV_HOME%\all-libs\geomutil.jar Prompt> javac -d classes -sourcepath src -cp %CP% src\olivsoftdesktopuserexits\*.javaMake sure you do not see any error.Prompt> bash Prompt> OLIV_HOME=/usr/OlivSoft Prompt> CP=$OLIV_HOME/all-3rd-party/mail.jar Prompt> CP=$CP:$OLIV_HOME/all-libs/nmeaparser.jar Prompt> CP=$CP:$OLIV_HOME/all-libs/nmeareader.jar Prompt> CP=$CP:$OLIV_HOME/all-libs/desktop.jar Prompt> CP=$CP:$OLIV_HOME/all-libs/geomutil.jar Prompt> javac -d classes -sourcepath src -cp $CP src/olivsoftdesktopuserexits/*.java -
Archive the generated classes:
On WindowsOn LinuxPrompt> cd classes Prompt> jar -cvf ..\emailUserExit.jar *Prompt> cd classes Prompt> jar -cvf ../emailUserExit.jar * -
Copy the archive in the
all-user-exitsdirectory
On WindowsOn LinuxPrompt> cd .. Prompt> copy *.jar %OLIV_HOME%\all-user-exitsCopyPrompt> cd .. Prompt> cp *.jar $OLIV_HOME/all-user-exitsemail.propertiesin theall-scriptsdirectory
On WindowsOn LinuxPrompt> copy email.properties %OLIV_HOME%\all-scriptsYou are almost done...Prompt> cp email.properties $OLIV_HOME/all-scripts -
Modify the line that starts the console, so it takes your work in account:
On WindowsOn Linuxset COMMAND=java %JAVA_OPTIONS% -classpath %CP% olivsoftdesktop.OlivSoftDesktop %HEADLESS_OPTIONS% -ue:olivsoftdesktopuserexits.DesktopEmailSender start "Headless Console (User-Exit)" %COMMAND%That's it!java $JAVA_OPTIONS -classpath $CP olivsoftdesktop.OlivSoftDesktop $HEADLESS_OPTIONS -ue:olivsoftdesktopuserexits.DesktopEmailSender &
Important: Do not forget to edit and modifyemail.properties, so it matches your environment, and your needs.
No comments:
Post a Comment