アルファ版。新版→https://osdn.jp/users/tacticsrealize/pf/ChlorophyllUploader/wiki/FrontPage
Révision | 82b375367456635f711775be0d76dcafef88ca32 (tree) |
---|---|
l'heure | 2015-07-08 20:15:01 |
Auteur | <15b05@15b0...> |
新ファイル形式に対応しArduinoとの連携
@@ -1,18 +1,11 @@ | ||
1 | 1 | package ants; |
2 | 2 | |
3 | 3 | import java.io.File; |
4 | -import java.io.FileInputStream; | |
5 | -import java.io.InputStreamReader; | |
6 | -import java.time.LocalDateTime; | |
7 | 4 | import java.time.format.DateTimeFormatter; |
8 | -import java.util.ArrayList; | |
9 | -import java.util.List; | |
10 | -import java.util.stream.Collectors; | |
11 | -import java.util.stream.Stream; | |
12 | 5 | |
13 | 6 | import mirrg.file.watcherspawning.FileWatcherSpawning; |
14 | - | |
15 | -import com.opencsv.CSVReader; | |
7 | +import ants.chlorofilsender.BuilderMessage; | |
8 | +import ants.chlorofilsender.PacketChlorofilSender; | |
16 | 9 | |
17 | 10 | public class SampleFileWatcherSpawning |
18 | 11 | { |
@@ -20,13 +13,13 @@ | ||
20 | 13 | /** |
21 | 14 | * ファイル名 |
22 | 15 | */ |
23 | - private final static DateTimeFormatter formatterIn = | |
16 | + public final static DateTimeFormatter formatterIn = | |
24 | 17 | DateTimeFormatter.ofPattern("uuuuMMddHHmmss"); |
25 | 18 | |
26 | 19 | /** |
27 | 20 | * 保存・Arduino送信形式 |
28 | 21 | */ |
29 | - private final static DateTimeFormatter formatterOut = | |
22 | + public final static DateTimeFormatter formatterOut = | |
30 | 23 | DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss"); |
31 | 24 | |
32 | 25 | public static void main(String[] args) |
@@ -37,75 +30,31 @@ | ||
37 | 30 | formatterIn, |
38 | 31 | formatterOut, |
39 | 32 | (file, localDateTime) -> { |
40 | - try (CSVReader csvReader = new CSVReader(new InputStreamReader( | |
41 | - new FileInputStream(file)))) { | |
42 | 33 | |
43 | - csvReader.forEach(csvLine -> | |
44 | - SampleFileWatcherSpawning.onData(file, localDateTime, csvLine)); | |
34 | + try { | |
35 | + PacketChlorofilSender.parse(localDateTime, file) | |
36 | + .forEach(SampleFileWatcherSpawning::onData); | |
37 | + } catch (Exception e) { | |
38 | + e.printStackTrace(); | |
45 | 39 | } |
40 | + | |
46 | 41 | }); |
47 | 42 | |
43 | + // initialized!! | |
44 | + | |
45 | + System.out.println(BuilderMessage.setPrefix( | |
46 | + "http://j.kisarazu.ac.jp/Arduino/ANTS/0301/")); | |
47 | + | |
48 | 48 | fileWatcherSpawning.start(); |
49 | 49 | |
50 | 50 | } |
51 | 51 | |
52 | - private static void onData(File file, LocalDateTime localDateTime, String[] csvLine) | |
52 | + protected static void onData(PacketChlorofilSender packet) | |
53 | 53 | { |
54 | - // CSV生カットデータがくる | |
55 | - | |
56 | - // カットデータの長さチェック | |
57 | - if (csvLine.length != 15) { | |
58 | - System.err.println("illegal size of columns: " | |
59 | - + file | |
60 | - + " (content: [" | |
61 | - + String.join(", ", csvLine) | |
62 | - + "])"); | |
63 | - return; | |
64 | - } | |
65 | - | |
66 | - // カットデータの整形と型変換 | |
67 | - List<Double> doubles; | |
68 | - try { | |
69 | - doubles = Stream.of(csvLine) | |
70 | - .map(String::trim) | |
71 | - .map(Double::parseDouble) | |
72 | - .collect(Collectors.toList()); | |
73 | - } catch (NumberFormatException e) { | |
74 | - System.err.println("illegal input string: '" | |
75 | - + e.getMessage() | |
76 | - + "', file: " | |
77 | - + file | |
78 | - + ""); | |
79 | - return; | |
80 | - } | |
81 | - | |
82 | - // 出力用文字列群のビルド | |
83 | - ArrayList<String> outputs = new ArrayList<>(); | |
84 | - outputs.add(formatterOut.format(localDateTime)); | |
85 | - doubles.stream() | |
86 | - .map(output -> output.toString()) | |
87 | - .forEach(outputs::add); | |
88 | - | |
89 | - // 出力用文字列群→出力用1行CSV | |
90 | - String outputCsv = outputs.stream() | |
91 | - .collect(Collectors.joining(",")); | |
92 | - | |
93 | - // 出力 | |
94 | - onData(file, localDateTime, outputCsv); | |
95 | - | |
96 | - } | |
97 | - | |
98 | - protected static void onData(File file, | |
99 | - LocalDateTime localDateTime, | |
100 | - String outputCsv) | |
101 | - { | |
102 | - | |
103 | - System.out.println("Processed!!: '" | |
104 | - + file | |
105 | - + "', " | |
106 | - + localDateTime.format(formatterOut)); | |
107 | - System.out.println(outputCsv); | |
108 | - | |
54 | + System.out.println(String.format( | |
55 | + "Processed!!: '%s'", | |
56 | + packet.time.format(formatterOut))); | |
57 | + System.out.println(BuilderMessage.build(packet)); | |
109 | 58 | } |
110 | 59 | |
111 | 60 | } |
@@ -0,0 +1,199 @@ | ||
1 | +package ants.chlorofilsender; | |
2 | + | |
3 | +import static org.junit.Assert.*; | |
4 | + | |
5 | +import java.time.LocalDateTime; | |
6 | +import java.util.function.Consumer; | |
7 | +import java.util.regex.Matcher; | |
8 | +import java.util.regex.Pattern; | |
9 | + | |
10 | +import org.junit.Test; | |
11 | + | |
12 | +public class BuilderMessage | |
13 | +{ | |
14 | + | |
15 | + @Test | |
16 | + public void test_build() | |
17 | + { | |
18 | + PacketChlorofilSender packet = new PacketChlorofilSender(); | |
19 | + | |
20 | + packet.time = LocalDateTime.of(2015, 11, 4, 22, 56, 8); | |
21 | + packet.channels[0].set(1.1, 1.2); | |
22 | + packet.channels[1].set(2.1, 2.2); | |
23 | + packet.channels[2].set(3.1, 3.2); | |
24 | + packet.channels[3].set(4.1, 4.2); | |
25 | + packet.channels[4].set(5.1, 5.2); | |
26 | + packet.channels[5].set(6.1, 6.2); | |
27 | + packet.average.set(7.1, 7.2); | |
28 | + packet.height = 8.1; | |
29 | + | |
30 | + String res = "" | |
31 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH1/P;S1.1;M" + "\n" | |
32 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH1/S;S1.2;M" + "\n" | |
33 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH2/P;S2.1;M" + "\n" | |
34 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH2/S;S2.2;M" + "\n" | |
35 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH3/P;S3.1;M" + "\n" | |
36 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH3/S;S3.2;M" + "\n" | |
37 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH4/P;S4.1;M" + "\n" | |
38 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH4/S;S4.2;M" + "\n" | |
39 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH5/P;S5.1;M" + "\n" | |
40 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH5/S;S5.2;M" + "\n" | |
41 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH6/P;S6.1;M" + "\n" | |
42 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SCH6/S;S6.2;M" + "\n" | |
43 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SAverage/P;S7.1;M" + "\n" | |
44 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SAverage/S;S7.2;M" + "\n" | |
45 | + + "C;SPushFiapPacket;I2015;I11;I4;I22;I56;I8;SHeight;S8.1;M" + "\n" | |
46 | + + "C;SFlushFiapPackets;M" + "\n"; | |
47 | + | |
48 | + assertEquals(res, | |
49 | + build(packet)); | |
50 | + } | |
51 | + | |
52 | + public static String build(PacketChlorofilSender packet) | |
53 | + { | |
54 | + StringBuilder sb = new StringBuilder(); | |
55 | + | |
56 | + sb.append(sendPacket(packet.time, "CH1/P", "" + packet.channels[0].p)); | |
57 | + sb.append(sendPacket(packet.time, "CH1/S", "" + packet.channels[0].s)); | |
58 | + sb.append(sendPacket(packet.time, "CH2/P", "" + packet.channels[1].p)); | |
59 | + sb.append(sendPacket(packet.time, "CH2/S", "" + packet.channels[1].s)); | |
60 | + sb.append(sendPacket(packet.time, "CH3/P", "" + packet.channels[2].p)); | |
61 | + sb.append(sendPacket(packet.time, "CH3/S", "" + packet.channels[2].s)); | |
62 | + sb.append(sendPacket(packet.time, "CH4/P", "" + packet.channels[3].p)); | |
63 | + sb.append(sendPacket(packet.time, "CH4/S", "" + packet.channels[3].s)); | |
64 | + sb.append(sendPacket(packet.time, "CH5/P", "" + packet.channels[4].p)); | |
65 | + sb.append(sendPacket(packet.time, "CH5/S", "" + packet.channels[4].s)); | |
66 | + sb.append(sendPacket(packet.time, "CH6/P", "" + packet.channels[5].p)); | |
67 | + sb.append(sendPacket(packet.time, "CH6/S", "" + packet.channels[5].s)); | |
68 | + sb.append(sendPacket(packet.time, "Average/P", "" + packet.average.p)); | |
69 | + sb.append(sendPacket(packet.time, "Average/S", "" + packet.average.s)); | |
70 | + sb.append(sendPacket(packet.time, "Height", "" + packet.height)); | |
71 | + sb.append(flush()); | |
72 | + | |
73 | + return sb.toString(); | |
74 | + } | |
75 | + | |
76 | + @Test | |
77 | + public void test_sendPacket() | |
78 | + { | |
79 | + assertEquals( | |
80 | + "C;SPushFiapPacket;I2015;I6;I30;I20;I9;I24;STest;S234;M\n", | |
81 | + sendPacket( | |
82 | + LocalDateTime.of(2015, 6, 30, 20, 9, 24), | |
83 | + "Test", "234")); | |
84 | + } | |
85 | + | |
86 | + public static String sendPacket( | |
87 | + LocalDateTime time, | |
88 | + String path, String value) | |
89 | + { | |
90 | + return callMethod("PushFiapPacket", consumer -> { | |
91 | + consumer.accept(time.getYear()); | |
92 | + consumer.accept(time.getMonthValue()); | |
93 | + consumer.accept(time.getDayOfMonth()); | |
94 | + consumer.accept(time.getHour()); | |
95 | + consumer.accept(time.getMinute()); | |
96 | + consumer.accept(time.getSecond()); | |
97 | + | |
98 | + consumer.accept(path); | |
99 | + consumer.accept(value); | |
100 | + }); | |
101 | + } | |
102 | + | |
103 | + @Test | |
104 | + public void test_flush() | |
105 | + { | |
106 | + assertEquals( | |
107 | + "C;SFlushFiapPackets;M\n", | |
108 | + flush()); | |
109 | + } | |
110 | + | |
111 | + public static String flush() | |
112 | + { | |
113 | + return callMethod("FlushFiapPackets", consumer -> { | |
114 | + }); | |
115 | + } | |
116 | + | |
117 | + @Test | |
118 | + public void test_setPrefix() | |
119 | + { | |
120 | + assertEquals( | |
121 | + "C;SSetFiapIdPrefix;Shttp://www.google.com/Test/;M\n", | |
122 | + setPrefix("http://www.google.com/Test/")); | |
123 | + } | |
124 | + | |
125 | + public static String setPrefix(String string) | |
126 | + { | |
127 | + return callMethod("SetFiapIdPrefix", consumer -> { | |
128 | + consumer.accept(string); | |
129 | + }); | |
130 | + } | |
131 | + | |
132 | + @Test | |
133 | + public void test_callMethod() | |
134 | + { | |
135 | + assertEquals( | |
136 | + "C;SMeth;D4.5;I79;D0.0;I0;S68;S;S/f4b[@\\\\4,].5o@s,\\;4e\\\n0o;M\n", | |
137 | + callMethod("Meth", consumer -> { | |
138 | + consumer.accept(4.5); | |
139 | + consumer.accept(79); | |
140 | + consumer.accept(0.0); | |
141 | + consumer.accept(0); | |
142 | + consumer.accept("68"); | |
143 | + consumer.accept(""); | |
144 | + consumer.accept("/f4b[@\\4,].5o@s,;4e\n0o"); | |
145 | + })); | |
146 | + } | |
147 | + | |
148 | + protected static final Pattern patternOperatorBreak = | |
149 | + Pattern.compile("([\\n;\\\\])"); | |
150 | + | |
151 | + public static String callMethod(String name, Consumer<IConsumerArgument> arguments) | |
152 | + { | |
153 | + StringBuilder sb = new StringBuilder(); | |
154 | + | |
155 | + sb.append("C;"); | |
156 | + IConsumerArgument consumerArgument = new IConsumerArgument() { | |
157 | + | |
158 | + @Override | |
159 | + public void accept(int argument) | |
160 | + { | |
161 | + sb.append("I" + argument + ";"); | |
162 | + } | |
163 | + | |
164 | + @Override | |
165 | + public void accept(double argument) | |
166 | + { | |
167 | + sb.append("D" + argument + ";"); | |
168 | + } | |
169 | + | |
170 | + @Override | |
171 | + public void accept(String argument) | |
172 | + { | |
173 | + Matcher matcher = patternOperatorBreak.matcher(argument); | |
174 | + sb.append("S" | |
175 | + + matcher.replaceAll("\\\\$1") | |
176 | + + ";"); | |
177 | + } | |
178 | + | |
179 | + }; | |
180 | + consumerArgument.accept(name); | |
181 | + arguments.accept(consumerArgument); | |
182 | + sb.append("M"); | |
183 | + sb.append("\n"); | |
184 | + | |
185 | + return sb.toString(); | |
186 | + } | |
187 | + | |
188 | + public interface IConsumerArgument | |
189 | + { | |
190 | + | |
191 | + public void accept(int argument); | |
192 | + | |
193 | + public void accept(double argument); | |
194 | + | |
195 | + public void accept(String argument); | |
196 | + | |
197 | + } | |
198 | + | |
199 | +} |
@@ -0,0 +1,172 @@ | ||
1 | +package ants.chlorofilsender; | |
2 | + | |
3 | +import java.awt.Component; | |
4 | +import java.time.LocalDateTime; | |
5 | +import java.time.ZoneId; | |
6 | +import java.util.ArrayList; | |
7 | +import java.util.Date; | |
8 | +import java.util.function.Consumer; | |
9 | + | |
10 | +import javax.swing.JButton; | |
11 | +import javax.swing.JCheckBox; | |
12 | +import javax.swing.JLabel; | |
13 | +import javax.swing.JSpinner; | |
14 | +import javax.swing.SpinnerDateModel; | |
15 | +import javax.swing.SpinnerNumberModel; | |
16 | +import javax.swing.Timer; | |
17 | + | |
18 | +import jp.hishidama.swing.layout.GroupLayoutUtil; | |
19 | +import mirrg.util.FrameMirrg; | |
20 | +import mirrg.util.Tuple; | |
21 | + | |
22 | +public class FrameChlorofilPacket extends FrameMirrg | |
23 | +{ | |
24 | + | |
25 | + public FrameChlorofilPacket(Consumer<PacketChlorofilSender> consumerPacket) | |
26 | + { | |
27 | + super("ChlorofilPacket"); | |
28 | + | |
29 | + { | |
30 | + JCheckBox checkBox1 = new JCheckBox("現在時刻に同期", true); | |
31 | + Tuple<JLabel, JSpinner> spinnerTime = createSpinnerRowDate("Time"); | |
32 | + | |
33 | + // P S | |
34 | + ArrayList<JSpinner[]> listSpinners = new ArrayList<>(); | |
35 | + for (int i = 0; i < 6; i++) { | |
36 | + listSpinners.add(createSpinnersRowDouble()); | |
37 | + } | |
38 | + JSpinner[] spinnersAverage = createSpinnersRowDouble(); | |
39 | + | |
40 | + Tuple<JLabel, JSpinner> spinnerHeight = createSpinnerRowDouble("Height", 0, -10000, 10000, 0.1); | |
41 | + | |
42 | + JButton buttonSend = new JButton("Send!"); | |
43 | + JButton buttonRandomize = new JButton("Randomize"); | |
44 | + | |
45 | + // | |
46 | + | |
47 | + buttonSend.addActionListener(e -> { | |
48 | + consumerPacket.accept(new PacketChlorofilSender( | |
49 | + LocalDateTime.ofInstant( | |
50 | + ((Date) spinnerTime.getY().getValue()).toInstant(), | |
51 | + ZoneId.systemDefault()), | |
52 | + ((Double) listSpinners.get(0)[0].getValue()), | |
53 | + ((Double) listSpinners.get(0)[1].getValue()), | |
54 | + ((Double) listSpinners.get(1)[0].getValue()), | |
55 | + ((Double) listSpinners.get(1)[1].getValue()), | |
56 | + ((Double) listSpinners.get(2)[0].getValue()), | |
57 | + ((Double) listSpinners.get(2)[1].getValue()), | |
58 | + ((Double) listSpinners.get(3)[0].getValue()), | |
59 | + ((Double) listSpinners.get(3)[1].getValue()), | |
60 | + ((Double) listSpinners.get(4)[0].getValue()), | |
61 | + ((Double) listSpinners.get(4)[1].getValue()), | |
62 | + ((Double) listSpinners.get(5)[0].getValue()), | |
63 | + ((Double) listSpinners.get(5)[1].getValue()), | |
64 | + ((Double) spinnersAverage[0].getValue()), | |
65 | + ((Double) spinnersAverage[1].getValue()), | |
66 | + ((Double) spinnerHeight.getY().getValue()))); | |
67 | + }); | |
68 | + | |
69 | + buttonRandomize.addActionListener(e -> { | |
70 | + listSpinners.forEach(spinners -> { | |
71 | + spinners[0].setValue(Math.random() * 500); | |
72 | + spinners[1].setValue(Math.random() * 500); | |
73 | + }); | |
74 | + spinnersAverage[0].setValue(Math.random() * 500); | |
75 | + spinnersAverage[1].setValue(Math.random() * 500); | |
76 | + spinnerHeight.getY().setValue(Math.random() * 500); | |
77 | + }); | |
78 | + | |
79 | + { | |
80 | + Timer timer1 = new Timer(1000, e -> { | |
81 | + if (!checkBox1.isSelected()) return; | |
82 | + | |
83 | + LocalDateTime value = LocalDateTime.now(); | |
84 | + spinnerTime.getY().getModel().setValue( | |
85 | + Date.from( | |
86 | + value.atZone(ZoneId.systemDefault()).toInstant())); | |
87 | + }); | |
88 | + timer1.start(); | |
89 | + | |
90 | + onClosed(e -> { | |
91 | + timer1.stop(); | |
92 | + }); | |
93 | + } | |
94 | + | |
95 | + { | |
96 | + ArrayList<Component[]> arrayComponents = new ArrayList<>(); | |
97 | + arrayComponents.add(new Component[] { | |
98 | + null, checkBox1, GroupLayoutUtil.SAME_L, | |
99 | + }); | |
100 | + arrayComponents.add(new Component[] { | |
101 | + spinnerTime.getX(), spinnerTime.getY(), GroupLayoutUtil.SAME_L, | |
102 | + }); | |
103 | + arrayComponents.add(new Component[] { | |
104 | + null, new JLabel("P"), new JLabel("S"), | |
105 | + }); | |
106 | + for (int i = 0; i < listSpinners.size(); i++) { | |
107 | + JSpinner[] spinners = listSpinners.get(i); | |
108 | + arrayComponents.add(new Component[] { | |
109 | + new JLabel("CH" + i), spinners[0], spinners[1], | |
110 | + }); | |
111 | + } | |
112 | + arrayComponents.add(new Component[] { | |
113 | + new JLabel("Average"), spinnersAverage[0], spinnersAverage[1], | |
114 | + }); | |
115 | + arrayComponents.add(new Component[] { | |
116 | + spinnerHeight.getX(), spinnerHeight.getY(), GroupLayoutUtil.SAME_L, | |
117 | + }); | |
118 | + arrayComponents.add(new Component[] { | |
119 | + null, buttonSend, buttonRandomize, | |
120 | + }); | |
121 | + | |
122 | + Component[][] components = arrayComponents.toArray(new Component[0][]); | |
123 | + | |
124 | + GroupLayoutUtil groupLayoutUtil = new GroupLayoutUtil(); | |
125 | + | |
126 | + groupLayoutUtil.setComponents(components); | |
127 | + groupLayoutUtil.setGroupLayoutTo(this.getContentPane()); | |
128 | + | |
129 | + groupLayoutUtil.getGroupLayout().setAutoCreateContainerGaps(true); | |
130 | + groupLayoutUtil.getGroupLayout().setAutoCreateGaps(true); | |
131 | + } | |
132 | + } | |
133 | + | |
134 | + prepareFrame(); | |
135 | + } | |
136 | + | |
137 | + private static Tuple<JLabel, JSpinner> createSpinnerRowDouble( | |
138 | + String labelText, double value, double min, double max, double step) | |
139 | + { | |
140 | + return new Tuple<>(new JLabel(labelText), createSpinnerRowDouble(value, min, max, step)); | |
141 | + } | |
142 | + | |
143 | + private static JSpinner createSpinnerRowDouble( | |
144 | + double value, double min, double max, double step) | |
145 | + { | |
146 | + JSpinner spinner = new JSpinner(new SpinnerNumberModel(value, min, max, step)); | |
147 | + spinner.setEditor(new JSpinner.NumberEditor(spinner)); | |
148 | + | |
149 | + return spinner; | |
150 | + } | |
151 | + | |
152 | + private static JSpinner[] createSpinnersRowDouble() | |
153 | + { | |
154 | + return new JSpinner[] { | |
155 | + createSpinnerRowDouble(0, -10000, 10000, 0.1), | |
156 | + createSpinnerRowDouble(0, -10000, 10000, 0.1), | |
157 | + }; | |
158 | + } | |
159 | + | |
160 | + private static final String dateFormat = "yyyy/MM/dd HH:mm:ss"; | |
161 | + | |
162 | + private static Tuple<JLabel, JSpinner> createSpinnerRowDate(String labelText) | |
163 | + { | |
164 | + JLabel label = new JLabel(labelText); | |
165 | + | |
166 | + JSpinner spinner = new JSpinner(new SpinnerDateModel()); | |
167 | + spinner.setEditor(new JSpinner.DateEditor(spinner, dateFormat)); | |
168 | + | |
169 | + return new Tuple<>(label, spinner); | |
170 | + } | |
171 | + | |
172 | +} |
@@ -0,0 +1,101 @@ | ||
1 | +package ants.chlorofilsender; | |
2 | + | |
3 | +import java.io.InputStream; | |
4 | +import java.util.ArrayList; | |
5 | +import java.util.function.BiConsumer; | |
6 | +import java.util.function.Consumer; | |
7 | +import java.util.function.IntConsumer; | |
8 | + | |
9 | +import mirrg.util.UTF8StreamReader; | |
10 | + | |
11 | +public class InputChlorofilSender extends UTF8StreamReader | |
12 | +{ | |
13 | + | |
14 | + public InputChlorofilSender(InputStream in, boolean blocking) | |
15 | + { | |
16 | + super(in, blocking); | |
17 | + } | |
18 | + | |
19 | + protected ArrayList<String> messageBlockBuffer; | |
20 | + protected String messageBlockSentinel = null; | |
21 | + | |
22 | + public void stream( | |
23 | + IntConsumer consumerAcceptedChar, | |
24 | + BiConsumer<EnumTypeMessage, String> consumerMessage, | |
25 | + Consumer<ArrayList<String>> consumerMessageBlock, | |
26 | + Consumer<Exception> consumerInternalException) | |
27 | + { | |
28 | + stream((Consumer<String>) line -> { | |
29 | + | |
30 | + if (messageBlockSentinel != null) { | |
31 | + if (line.equals(messageBlockSentinel)) { | |
32 | + messageBlockSentinel = null; | |
33 | + consumerMessageBlock.accept(messageBlockBuffer); | |
34 | + messageBlockBuffer = new ArrayList<>(); | |
35 | + } else { | |
36 | + messageBlockBuffer.add(line); | |
37 | + } | |
38 | + | |
39 | + return; | |
40 | + } | |
41 | + | |
42 | + { | |
43 | + String res; | |
44 | + | |
45 | + if ((res = getProperty(line, "C")) != null) { | |
46 | + int i; | |
47 | + try { | |
48 | + i = Integer.parseInt(res, 10); | |
49 | + } catch (NumberFormatException e) { | |
50 | + consumerInternalException.accept(e); | |
51 | + return; | |
52 | + } | |
53 | + consumerAcceptedChar.accept(i); | |
54 | + return; | |
55 | + } | |
56 | + if ((res = getProperty(line, "Message")) != null) { | |
57 | + consumerMessage.accept(EnumTypeMessage.MESSAGE, res); | |
58 | + return; | |
59 | + } | |
60 | + if ((res = getProperty(line, "Error")) != null) { | |
61 | + consumerMessage.accept(EnumTypeMessage.ERROR, res); | |
62 | + return; | |
63 | + } | |
64 | + if ((res = getProperty(line, "Warnings")) != null) { | |
65 | + consumerMessage.accept(EnumTypeMessage.WARNINGS, res); | |
66 | + return; | |
67 | + } | |
68 | + if ((res = getProperty(line, "MessageBlock")) != null) { | |
69 | + messageBlockSentinel = res; | |
70 | + messageBlockBuffer = new ArrayList<>(); | |
71 | + return; | |
72 | + } | |
73 | + | |
74 | + } | |
75 | + | |
76 | + }); | |
77 | + } | |
78 | + | |
79 | + protected String getProperty(String string, String prefix) | |
80 | + { | |
81 | + prefix += ":"; | |
82 | + | |
83 | + if (string.startsWith(prefix)) { | |
84 | + return string.substring(prefix.length()); | |
85 | + } else { | |
86 | + return null; | |
87 | + } | |
88 | + } | |
89 | + | |
90 | + public static enum EnumTypeMessage | |
91 | + { | |
92 | + MESSAGE, ERROR, WARNINGS, | |
93 | + } | |
94 | + | |
95 | + public void close() | |
96 | + { | |
97 | + // TODO 自動生成されたメソッド・スタブ | |
98 | + | |
99 | + } | |
100 | + | |
101 | +} |
@@ -0,0 +1,101 @@ | ||
1 | +package ants.chlorofilsender; | |
2 | + | |
3 | +import java.io.OutputStream; | |
4 | +import java.io.PrintStream; | |
5 | +import java.io.UnsupportedEncodingException; | |
6 | +import java.util.Iterator; | |
7 | +import java.util.LinkedList; | |
8 | +import java.util.function.IntConsumer; | |
9 | + | |
10 | +public class OutputChlorofilSender | |
11 | +{ | |
12 | + | |
13 | + protected static final int ARDUINO_MAX_BUFFER = 63; | |
14 | + | |
15 | + protected PrintStream out; | |
16 | + | |
17 | + public OutputChlorofilSender(OutputStream outputStream) | |
18 | + { | |
19 | + try { | |
20 | + out = new PrintStream(outputStream, false, "UTF-8"); | |
21 | + } catch (UnsupportedEncodingException e) { | |
22 | + throw new RuntimeException(e); | |
23 | + } | |
24 | + } | |
25 | + | |
26 | + protected int sentChars = 0; | |
27 | + protected transient int acceptedChars = 0; | |
28 | + | |
29 | + public IntConsumer getConsumerAcceptedChars() | |
30 | + { | |
31 | + return acceptedChars -> { | |
32 | + this.acceptedChars = acceptedChars; | |
33 | + }; | |
34 | + } | |
35 | + | |
36 | + public void send(String string) | |
37 | + { | |
38 | + synchronized (sendingStrings) { | |
39 | + sendingStrings.add(string); | |
40 | + } | |
41 | + } | |
42 | + | |
43 | + protected LinkedList<String> sendingStrings = new LinkedList<>(); | |
44 | + | |
45 | + protected Thread thread; | |
46 | + | |
47 | + public Thread startSenderThread() | |
48 | + { | |
49 | + thread = new Thread(() -> { | |
50 | + try { | |
51 | + while (true) { | |
52 | + | |
53 | + synchronized (sendingStrings) { | |
54 | + for (Iterator<String> iterator = sendingStrings.iterator(); iterator.hasNext();) { | |
55 | + String string = iterator.next(); | |
56 | + sendImpl(string); | |
57 | + iterator.remove(); | |
58 | + } | |
59 | + } | |
60 | + | |
61 | + Thread.sleep(5); | |
62 | + } | |
63 | + } catch (InterruptedException e) { | |
64 | + } | |
65 | + }); | |
66 | + thread.setDaemon(true); | |
67 | + thread.start(); | |
68 | + return thread; | |
69 | + } | |
70 | + | |
71 | + protected void sendImpl(String string) throws InterruptedException | |
72 | + { | |
73 | + do { | |
74 | + int bufferedChars = sentChars - acceptedChars; | |
75 | + int bufferSpace = ARDUINO_MAX_BUFFER - bufferedChars; | |
76 | + int sendingChars = Math.min(string.length(), bufferSpace); | |
77 | + | |
78 | + sendImpl2(string.substring(0, sendingChars)); | |
79 | + string = string.substring(sendingChars); | |
80 | + | |
81 | + if (string.isEmpty()) break; | |
82 | + | |
83 | + Thread.sleep(5); | |
84 | + | |
85 | + } while (true); | |
86 | + } | |
87 | + | |
88 | + protected void sendImpl2(String string) | |
89 | + { | |
90 | + sentChars += string.length(); | |
91 | + out.print(string); | |
92 | + out.flush(); | |
93 | + } | |
94 | + | |
95 | + public void close() | |
96 | + { | |
97 | + thread.interrupt(); | |
98 | + out.close(); | |
99 | + } | |
100 | + | |
101 | +} |
@@ -0,0 +1,152 @@ | ||
1 | +package ants.chlorofilsender; | |
2 | + | |
3 | +import java.io.File; | |
4 | +import java.io.FileInputStream; | |
5 | +import java.io.IOException; | |
6 | +import java.io.InputStream; | |
7 | +import java.io.InputStreamReader; | |
8 | +import java.time.LocalDateTime; | |
9 | +import java.util.ArrayList; | |
10 | +import java.util.List; | |
11 | +import java.util.stream.Collectors; | |
12 | +import java.util.stream.Stream; | |
13 | + | |
14 | +import com.opencsv.CSVParser; | |
15 | +import com.opencsv.CSVReader; | |
16 | + | |
17 | +public class PacketChlorofilSender | |
18 | +{ | |
19 | + | |
20 | + public LocalDateTime time; | |
21 | + public Channel[] channels = new Channel[6]; | |
22 | + public Channel average = new Channel(); | |
23 | + public double height; | |
24 | + | |
25 | + public PacketChlorofilSender() | |
26 | + { | |
27 | + for (int i = 0; i < channels.length; i++) { | |
28 | + channels[i] = new Channel(); | |
29 | + } | |
30 | + } | |
31 | + | |
32 | + public PacketChlorofilSender( | |
33 | + LocalDateTime time, | |
34 | + double ch1p, double ch1s, | |
35 | + double ch2p, double ch2s, | |
36 | + double ch3p, double ch3s, | |
37 | + double ch4p, double ch4s, | |
38 | + double ch5p, double ch5s, | |
39 | + double ch6p, double ch6s, | |
40 | + double averageP, double averageS, | |
41 | + double height) | |
42 | + { | |
43 | + this(); | |
44 | + | |
45 | + this.time = time; | |
46 | + channels[0].set(ch1p, ch1s); | |
47 | + channels[1].set(ch2p, ch2s); | |
48 | + channels[2].set(ch3p, ch3s); | |
49 | + channels[3].set(ch4p, ch4s); | |
50 | + channels[4].set(ch5p, ch5s); | |
51 | + channels[5].set(ch6p, ch6s); | |
52 | + average.set(averageP, averageS); | |
53 | + this.height = height; | |
54 | + } | |
55 | + | |
56 | + public static ArrayList<PacketChlorofilSender> parse(LocalDateTime time, File file) throws IOException, PacketChlorofilSenderExcception | |
57 | + { | |
58 | + return parse(time, new FileInputStream(file)); | |
59 | + } | |
60 | + | |
61 | + public static ArrayList<PacketChlorofilSender> parse(LocalDateTime time, InputStream in) throws IOException, PacketChlorofilSenderExcception | |
62 | + { | |
63 | + try (CSVReader csvReader = | |
64 | + new CSVReader(new InputStreamReader(in))) { | |
65 | + | |
66 | + ArrayList<PacketChlorofilSender> packets = new ArrayList<>(); | |
67 | + | |
68 | + for (String[] csvLine : csvReader) { | |
69 | + packets.add(parse(time, csvLine)); | |
70 | + } | |
71 | + | |
72 | + return packets; | |
73 | + } | |
74 | + } | |
75 | + | |
76 | + public static PacketChlorofilSender parse(LocalDateTime time, String csv) throws IOException, PacketChlorofilSenderExcception | |
77 | + { | |
78 | + CSVParser parser = new CSVParser(); | |
79 | + return parse(time, parser.parseLineMulti(csv)); | |
80 | + } | |
81 | + | |
82 | + public static class PacketChlorofilSenderExcception extends Exception | |
83 | + { | |
84 | + | |
85 | + public PacketChlorofilSenderExcception(String string) | |
86 | + { | |
87 | + super(string); | |
88 | + } | |
89 | + | |
90 | + public PacketChlorofilSenderExcception(NumberFormatException e) | |
91 | + { | |
92 | + super(e); | |
93 | + } | |
94 | + | |
95 | + } | |
96 | + | |
97 | + public static PacketChlorofilSender parse(LocalDateTime time, String[] csvLine) throws PacketChlorofilSenderExcception | |
98 | + { | |
99 | + // CSV生カットデータがくる | |
100 | + | |
101 | + // カットデータの長さチェック | |
102 | + if (csvLine.length != 15) { | |
103 | + throw new PacketChlorofilSenderExcception("illegal size of columns: " | |
104 | + + csvLine.length | |
105 | + + " != 15"); | |
106 | + } | |
107 | + | |
108 | + // カットデータの整形と型変換 | |
109 | + List<Double> doubles; | |
110 | + try { | |
111 | + doubles = Stream.of(csvLine) | |
112 | + .map(String::trim) | |
113 | + .map(Double::parseDouble) | |
114 | + .collect(Collectors.toList()); | |
115 | + } catch (NumberFormatException e) { | |
116 | + throw new PacketChlorofilSenderExcception(e); | |
117 | + } | |
118 | + | |
119 | + // build | |
120 | + return new PacketChlorofilSender( | |
121 | + time, | |
122 | + doubles.get(0), | |
123 | + doubles.get(1), | |
124 | + doubles.get(2), | |
125 | + doubles.get(3), | |
126 | + doubles.get(4), | |
127 | + doubles.get(5), | |
128 | + doubles.get(6), | |
129 | + doubles.get(7), | |
130 | + doubles.get(8), | |
131 | + doubles.get(9), | |
132 | + doubles.get(10), | |
133 | + doubles.get(11), | |
134 | + doubles.get(12), | |
135 | + doubles.get(13), | |
136 | + doubles.get(14)); | |
137 | + | |
138 | + } | |
139 | + | |
140 | + public static class Channel | |
141 | + { | |
142 | + public double p; | |
143 | + public double s; | |
144 | + | |
145 | + public void set(double p, double s) | |
146 | + { | |
147 | + this.p = p; | |
148 | + this.s = s; | |
149 | + } | |
150 | + } | |
151 | + | |
152 | +} |
@@ -0,0 +1,160 @@ | ||
1 | +package ants.chlorofilsender.test; | |
2 | + | |
3 | +import gnu.io.CommPortIdentifier; | |
4 | +import gnu.io.PortInUseException; | |
5 | +import gnu.io.RXTXPort; | |
6 | +import gnu.io.SerialPort; | |
7 | +import gnu.io.UnsupportedCommOperationException; | |
8 | + | |
9 | +import java.io.File; | |
10 | + | |
11 | +import mirrg.file.watcherspawning.FileWatcherSpawning; | |
12 | +import ants.Main; | |
13 | +import ants.SampleFileWatcherSpawning; | |
14 | +import ants.chlorofilsender.BuilderMessage; | |
15 | +import ants.chlorofilsender.FrameChlorofilPacket; | |
16 | +import ants.chlorofilsender.InputChlorofilSender; | |
17 | +import ants.chlorofilsender.OutputChlorofilSender; | |
18 | +import ants.chlorofilsender.PacketChlorofilSender; | |
19 | +import ants.gui.frames.FrameSelectPort; | |
20 | + | |
21 | +public class SampleChlorofilSending | |
22 | +{ | |
23 | + | |
24 | + public static void main(String[] args) | |
25 | + { | |
26 | + Main.init(); | |
27 | + new FrameSelectPort(SampleChlorofilSending::start).setVisible(true); | |
28 | + } | |
29 | + | |
30 | + public static void start(CommPortIdentifier portIdentifier) | |
31 | + { | |
32 | + RXTXPort port; | |
33 | + try { | |
34 | + port = (RXTXPort) portIdentifier.open("ANTS_FILE", 2000); | |
35 | + } catch (PortInUseException e) { | |
36 | + e.printStackTrace(); | |
37 | + return; | |
38 | + } | |
39 | + | |
40 | + try { | |
41 | + port.setSerialPortParams( | |
42 | + 9600, | |
43 | + SerialPort.DATABITS_8, | |
44 | + SerialPort.STOPBITS_1, | |
45 | + SerialPort.PARITY_NONE); | |
46 | + port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); | |
47 | + } catch (UnsupportedCommOperationException e) { | |
48 | + e.printStackTrace(); | |
49 | + return; | |
50 | + } | |
51 | + | |
52 | + System.out.println("Serial Connected"); | |
53 | + | |
54 | + /////////////////////////////////////////////////// | |
55 | + | |
56 | + OutputChlorofilSender outputChlorofilSender = | |
57 | + new OutputChlorofilSender(port.getOutputStream()); | |
58 | + | |
59 | + InputChlorofilSender inputChlorofilSender = | |
60 | + new InputChlorofilSender(port.getInputStream(), false); | |
61 | + | |
62 | + outputChlorofilSender.startSenderThread(); | |
63 | + | |
64 | + Thread threadInput; | |
65 | + { | |
66 | + threadInput = new Thread(() -> { | |
67 | + inputChlorofilSender.stream( | |
68 | + outputChlorofilSender.getConsumerAcceptedChars().andThen(acceptedChars -> { | |
69 | + System.out.println(String.format( | |
70 | + "AcceptedChars: %s", | |
71 | + acceptedChars)); | |
72 | + }), | |
73 | + (typeMessage, message) -> { | |
74 | + System.out.println(String.format( | |
75 | + "%s: %s", | |
76 | + typeMessage.name(), | |
77 | + message)); | |
78 | + }, | |
79 | + messageBlock -> { | |
80 | + System.out.println("MESSAGE_BLOCK >>>"); | |
81 | + messageBlock.forEach(string -> { | |
82 | + System.out.println(" " + string); | |
83 | + }); | |
84 | + }, | |
85 | + e -> { | |
86 | + e.printStackTrace(); | |
87 | + }); | |
88 | + }); | |
89 | + threadInput.setDaemon(true); | |
90 | + threadInput.start(); | |
91 | + } | |
92 | + | |
93 | + System.out.println("Initialized ChlorofilSender IO Wrapper"); | |
94 | + | |
95 | + //////////////////////////////////////// | |
96 | + | |
97 | + FrameChlorofilPacket frameChlorofilPacket = new FrameChlorofilPacket(packet -> { | |
98 | + outputChlorofilSender.send(BuilderMessage.build(packet)); | |
99 | + flush(outputChlorofilSender); | |
100 | + }); | |
101 | + | |
102 | + frameChlorofilPacket.onClosed(e -> { | |
103 | + threadInput.interrupt(); | |
104 | + outputChlorofilSender.close(); | |
105 | + port.close(); | |
106 | + }); | |
107 | + | |
108 | + System.out.println("Start: port=" + portIdentifier.getName()); | |
109 | + | |
110 | + FileWatcherSpawning fileWatcherSpawning = new FileWatcherSpawning( | |
111 | + new File("./csvs"), | |
112 | + new File("./config.txt"), | |
113 | + SampleFileWatcherSpawning.formatterIn, | |
114 | + SampleFileWatcherSpawning.formatterOut, | |
115 | + (file, localDateTime) -> { | |
116 | + | |
117 | + try { | |
118 | + PacketChlorofilSender.parse(localDateTime, file) | |
119 | + .forEach(packet -> { | |
120 | + outputChlorofilSender.send(BuilderMessage.build(packet)); | |
121 | + flush(outputChlorofilSender); | |
122 | + }); | |
123 | + } catch (Exception e) { | |
124 | + e.printStackTrace(); | |
125 | + } | |
126 | + | |
127 | + }); | |
128 | + | |
129 | + // initialized!! | |
130 | + | |
131 | + System.out.println(BuilderMessage.setPrefix( | |
132 | + "http://j.kisarazu.ac.jp/Arduino/ANTS/0301/")); | |
133 | + | |
134 | + fileWatcherSpawning.start(); | |
135 | + | |
136 | + ///////////////////////////////////////////////////// | |
137 | + | |
138 | + frameChlorofilPacket.setVisible(true); | |
139 | + | |
140 | + outputChlorofilSender.send( | |
141 | + "C;SResetSerialReader;M;"); | |
142 | + | |
143 | + try { | |
144 | + Thread.sleep(1000); | |
145 | + } catch (InterruptedException e) { | |
146 | + e.printStackTrace(); | |
147 | + return; | |
148 | + } | |
149 | + | |
150 | + outputChlorofilSender.send( | |
151 | + "C;SSetIdPrefix;Shttp://j.kisarazu.ac.jp/Arduino/ANTS/0630/;M;"); | |
152 | + | |
153 | + } | |
154 | + | |
155 | + private static void flush(OutputChlorofilSender outputChlorofilSender) | |
156 | + { | |
157 | + outputChlorofilSender.send("C;SFlushFiapPacket;M;"); | |
158 | + } | |
159 | + | |
160 | +} |
@@ -0,0 +1,19 @@ | ||
1 | +package ants.chlorofilsender.test; | |
2 | + | |
3 | +import ants.Main; | |
4 | +import ants.chlorofilsender.BuilderMessage; | |
5 | +import ants.chlorofilsender.FrameChlorofilPacket; | |
6 | + | |
7 | +public class SampleFrameChlorofilPacket | |
8 | +{ | |
9 | + | |
10 | + public static void main(String[] args) | |
11 | + { | |
12 | + Main.init(); | |
13 | + new FrameChlorofilPacket(packet -> { | |
14 | + String res = BuilderMessage.build(packet); | |
15 | + System.out.println(res); | |
16 | + }).setVisible(true); | |
17 | + } | |
18 | + | |
19 | +} |
@@ -0,0 +1,67 @@ | ||
1 | +package ants.chlorofilsender.test; | |
2 | + | |
3 | +import static org.junit.Assert.*; | |
4 | + | |
5 | +import java.io.ByteArrayInputStream; | |
6 | +import java.io.IOException; | |
7 | +import java.time.LocalDateTime; | |
8 | +import java.util.ArrayList; | |
9 | + | |
10 | +import org.junit.Test; | |
11 | + | |
12 | +import ants.chlorofilsender.PacketChlorofilSender; | |
13 | +import ants.chlorofilsender.PacketChlorofilSender.PacketChlorofilSenderExcception; | |
14 | + | |
15 | +public class TestPacketChlorofilSender | |
16 | +{ | |
17 | + | |
18 | + @Test | |
19 | + public void test_parse0() throws IOException, PacketChlorofilSenderExcception | |
20 | + { | |
21 | + ArrayList<PacketChlorofilSender> parse = PacketChlorofilSender.parse( | |
22 | + LocalDateTime.of(2015, 1, 2, 3, 4, 5), | |
23 | + new ByteArrayInputStream(CSV_TEST_PACKET.getBytes("utf-8"))); | |
24 | + | |
25 | + assertEquals(1, parse.size()); | |
26 | + assertPacket(parse.get(0)); | |
27 | + } | |
28 | + | |
29 | + @Test | |
30 | + public void test_parse1() throws IOException, PacketChlorofilSenderExcception | |
31 | + { | |
32 | + assertPacket(PacketChlorofilSender.parse( | |
33 | + LocalDateTime.of(2015, 1, 2, 3, 4, 5), | |
34 | + CSV_TEST_PACKET)); | |
35 | + } | |
36 | + | |
37 | + protected String CSV_TEST_PACKET = | |
38 | + "1.1, 1.2, 2.1, 2.2, 3.1, 3.2, 4.1, 4.2, 5.1, 5.2, 6.1, 6.2, 7.1, 7.2, 8.1"; | |
39 | + | |
40 | + protected void assertPacket(PacketChlorofilSender packet) | |
41 | + { | |
42 | + assertEquals(2015, packet.time.getYear()); | |
43 | + assertEquals(1, packet.time.getMonthValue()); | |
44 | + assertEquals(2, packet.time.getDayOfMonth()); | |
45 | + assertEquals(3, packet.time.getHour()); | |
46 | + assertEquals(4, packet.time.getMinute()); | |
47 | + assertEquals(5, packet.time.getSecond()); | |
48 | + | |
49 | + assertEquals(0.0001, 1.1, packet.channels[0].p); | |
50 | + assertEquals(0.0001, 1.2, packet.channels[0].s); | |
51 | + assertEquals(0.0001, 2.1, packet.channels[1].p); | |
52 | + assertEquals(0.0001, 2.2, packet.channels[1].s); | |
53 | + assertEquals(0.0001, 3.1, packet.channels[2].p); | |
54 | + assertEquals(0.0001, 3.2, packet.channels[2].s); | |
55 | + assertEquals(0.0001, 4.1, packet.channels[3].p); | |
56 | + assertEquals(0.0001, 4.2, packet.channels[3].s); | |
57 | + assertEquals(0.0001, 5.1, packet.channels[4].p); | |
58 | + assertEquals(0.0001, 5.2, packet.channels[4].s); | |
59 | + assertEquals(0.0001, 6.1, packet.channels[5].p); | |
60 | + assertEquals(0.0001, 6.2, packet.channels[5].s); | |
61 | + assertEquals(0.0001, 7.1, packet.average.p); | |
62 | + assertEquals(0.0001, 7.2, packet.average.s); | |
63 | + | |
64 | + assertEquals(0.0001, 8.1, packet.height); | |
65 | + } | |
66 | + | |
67 | +} |