Commit 43f91fb6411537add6b28aeae2f325edc95c5fbf
1 parent
20a8711d
--no commit message
Showing
7 changed files
with
303 additions
and
277 deletions
DUREX Vendor Control/CommunicationProtocol.h
@@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
9 | #import <Foundation/Foundation.h> | 9 | #import <Foundation/Foundation.h> |
10 | #import "EMFramework.h" | 10 | #import "EMFramework.h" |
11 | 11 | ||
12 | +#define SLEEP_MS ((int)100) | ||
12 | #define MAX_STRING_LENGTH ((int)200) | 13 | #define MAX_STRING_LENGTH ((int)200) |
13 | #define MAX_RETRIES ((int)3) | 14 | #define MAX_RETRIES ((int)3) |
14 | #define MAX_PRODUCT_NAME_LENGTH ((int)64) | 15 | #define MAX_PRODUCT_NAME_LENGTH ((int)64) |
@@ -22,10 +23,8 @@ | @@ -22,10 +23,8 @@ | ||
22 | 23 | ||
23 | @interface CommunicationProtocol : NSObject | 24 | @interface CommunicationProtocol : NSObject |
24 | 25 | ||
25 | --(Boolean) waitForMessageAvailableMobile: (Boolean) status; | ||
26 | --(Boolean) waitForMessageAvailableDevice: (Boolean) status; | ||
27 | --(Boolean) writeMessage: (NSString*) message; | ||
28 | --(NSString*) readMessage; | 26 | +-(void) writeMessage: (NSString*) message; |
27 | +-(void) readMessage; | ||
29 | -(Boolean) establishConnection; | 28 | -(Boolean) establishConnection; |
30 | -(Boolean) updateTime: (NSDateComponents*) date; | 29 | -(Boolean) updateTime: (NSDateComponents*) date; |
31 | -(Boolean) updatePrice: (uint8_t) channel : (uint8_t) product : (uint8_t) eur : (uint8_t) cents; | 30 | -(Boolean) updatePrice: (uint8_t) channel : (uint8_t) product : (uint8_t) eur : (uint8_t) cents; |
DUREX Vendor Control/CommunicationProtocol.m
@@ -12,6 +12,10 @@ | @@ -12,6 +12,10 @@ | ||
12 | 12 | ||
13 | @property Boolean messageAvailableMobile; | 13 | @property Boolean messageAvailableMobile; |
14 | @property Boolean messageAvailableDevice; | 14 | @property Boolean messageAvailableDevice; |
15 | +@property NSMutableString *message; | ||
16 | +@property NSUInteger currentIndex; | ||
17 | +@property NSUInteger remainingBytes; | ||
18 | +@property NSInteger numPackets; | ||
15 | 19 | ||
16 | @end | 20 | @end |
17 | 21 | ||
@@ -29,269 +33,284 @@ | @@ -29,269 +33,284 @@ | ||
29 | return shared; | 33 | return shared; |
30 | } | 34 | } |
31 | 35 | ||
32 | --(void) readMessageAvailableMobile | 36 | +- (void) reportError : (NSString*) error |
33 | { | 37 | { |
34 | - [[EMConnectionManager sharedManager] readResource:@"messageAvailableMobile" onSuccess:^(id readValue) | ||
35 | - { | ||
36 | - [self setMessageAvailableMobile:[readValue intValue]]; | ||
37 | - if([self messageAvailableMobile]) | ||
38 | - { | ||
39 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile read: TRUE"); | ||
40 | - } | ||
41 | - else | ||
42 | - { | ||
43 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile read: FALSE"); | ||
44 | - } | ||
45 | - } | ||
46 | - onFail:^(NSError *error) | ||
47 | - { | ||
48 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
49 | - }]; | 38 | + |
50 | } | 39 | } |
51 | 40 | ||
52 | --(void) readMessageAvailableDevice | 41 | +-(void) readMessage |
53 | { | 42 | { |
54 | - [[EMConnectionManager sharedManager] readResource:@"messageAvailableDevice" onSuccess:^(id readValue) | ||
55 | - { | ||
56 | - [self setMessageAvailableDevice:[readValue intValue]]; | ||
57 | - if([self messageAvailableDevice]) | ||
58 | - { | ||
59 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableDevice read: TRUE"); | ||
60 | - } | ||
61 | - else | ||
62 | - { | ||
63 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableDevice read: FALSE"); | ||
64 | - } | ||
65 | - } | ||
66 | - onFail:^(NSError *error) | ||
67 | - { | ||
68 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
69 | - }]; | 43 | + self.numPackets = -1; |
44 | + [self waitForMessage]; | ||
70 | } | 45 | } |
71 | 46 | ||
72 | --(Boolean) waitForMessageAvailableMobile: (Boolean) status | 47 | +- (void) readNextFragment |
73 | { | 48 | { |
74 | - uint8_t retries = 0; | ||
75 | - [self readMessageAvailableMobile]; | ||
76 | - while([self messageAvailableMobile] != status) | 49 | + __block NSUInteger numBytes; |
50 | + if(self.numPackets) | ||
77 | { | 51 | { |
78 | - [NSThread sleepForTimeInterval:5]; | ||
79 | - [self readMessageAvailableMobile]; | ||
80 | - if([self messageAvailableMobile] != status) | 52 | + [[EMConnectionManager sharedManager] readResource:@"numBytes" onSuccess:^(id readValue) |
81 | { | 53 | { |
82 | - if(retries++ == MAX_RETRIES) | 54 | + numBytes = [readValue unsignedCharValue]; |
55 | + NSLog(@"[CommunicationProtocol.m]: numBytes read: %d",numBytes); | ||
56 | + [[EMConnectionManager sharedManager] readResource:@"data" onSuccess:^(id readValue) | ||
83 | { | 57 | { |
84 | - NSLog(@"[CommunicationProtocol.m]: Timeout while waiting for answer"); | ||
85 | - return FALSE; | ||
86 | - } | ||
87 | - } | 58 | + NSString *readData = [readValue substringToIndex:numBytes]; |
59 | + if([readValue length] < numBytes) | ||
60 | + { | ||
61 | + NSLog(@"[CommunicationProtocol.m]: WARNING: Device issued wrong numBytes, possible truncated message."); | ||
62 | + } | ||
63 | + [self.message appendString:readData]; | ||
64 | + NSLog(@"[CommunicationProtocol.m]: data read: %@",readData); | ||
65 | + [[EMConnectionManager sharedManager] writeValue:@"0" toResource:@"messageAvailableDevice" onSuccess:^ | ||
66 | + { | ||
67 | + NSLog(@"[CommunicationProtocol.m]: messageAvailableDevice set to FALSE"); | ||
68 | + NSLog(@"[CommunicationProtocol.m]: packet read"); | ||
69 | + [self waitForMessage]; | ||
70 | + }onFail:^(NSError *error) | ||
71 | + { | ||
72 | + NSLog(@"[CommunicationProtocol.m]: On setMessageAvailableDevice to FALSE: %@",error); | ||
73 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
74 | + }]; | ||
75 | + }onFail:^(NSError *error) | ||
76 | + { | ||
77 | + NSLog(@"[CommunicationProtocol.m]: On readData: %@",error); | ||
78 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
79 | + }]; | ||
80 | + }onFail:^(NSError *error) | ||
81 | + { | ||
82 | + NSLog(@"[CommunicationProtocol.m]: On readNumBytes: %@",error); | ||
83 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
84 | + }]; | ||
85 | + } | ||
86 | + else | ||
87 | + { | ||
88 | + //processMessage | ||
88 | } | 89 | } |
89 | - return TRUE; | ||
90 | } | 90 | } |
91 | 91 | ||
92 | --(Boolean) waitForMessageAvailableDevice: (Boolean) status | 92 | +- (void) readNumPackets |
93 | { | 93 | { |
94 | - uint8_t retries = 0; | ||
95 | - [self readMessageAvailableDevice]; | ||
96 | - while([self messageAvailableDevice] != status) | 94 | + [[EMConnectionManager sharedManager] readResource:@"numPackets" onSuccess:^(id readValue) |
97 | { | 95 | { |
98 | - [NSThread sleepForTimeInterval:5]; | ||
99 | - [self readMessageAvailableDevice]; | ||
100 | - if([self messageAvailableDevice] != status) | ||
101 | - { | ||
102 | - if(retries++ == MAX_RETRIES) | ||
103 | - { | ||
104 | - NSLog(@"[CommunicationProtocol.m]: Timeout while waiting for answer"); | ||
105 | - return FALSE; | ||
106 | - } | ||
107 | - } | ||
108 | - } | ||
109 | - return TRUE; | 96 | + self.numPackets = [readValue unsignedCharValue]; |
97 | + NSLog(@"[CommunicationProtocol.m]: numPackets read: %d",self.numPackets); | ||
98 | + [self readNextFragment]; | ||
99 | + }onFail:^(NSError *error) | ||
100 | + { | ||
101 | + NSLog(@"[CommunicationProtocol.m]: On readNumPackets: %@",error); | ||
102 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
103 | + }]; | ||
110 | } | 104 | } |
111 | 105 | ||
112 | --(NSString*) readMessage | 106 | +- (void) waitForMessage |
113 | { | 107 | { |
114 | - __block uint8_t numPackets = 1, numBytes; //HACK! | ||
115 | - __block NSMutableString *message = [[NSMutableString alloc] init]; | ||
116 | - [message setString:@""]; | ||
117 | - //if([self waitForMessageAvailableDevice:TRUE]) | ||
118 | - if(1) //HACK! | ||
119 | - { | ||
120 | - [[EMConnectionManager sharedManager] readResource:@"numPackets" onSuccess:^(id readValue) | 108 | + NSLog(@"[CommunicationProtocol.m]: Reading messageAvailable from device"); |
109 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableDevice" onSuccess:^(id readValue) | ||
110 | + { | ||
111 | + [self setMessageAvailableDevice:[readValue intValue]]; | ||
112 | + if(![self messageAvailableDevice]) | ||
121 | { | 113 | { |
122 | - numPackets = (uint8_t) [readValue unsignedCharValue]; | ||
123 | - NSLog(@"[CommunicationProtocol.m]: numPackets read: %d",numPackets); | 114 | + NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying..."); |
115 | + [NSThread sleepForTimeInterval:SLEEP_MS]; | ||
116 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableDevice" onSuccess:^(id readValue) | ||
117 | + { | ||
118 | + [self setMessageAvailableDevice:[readValue intValue]]; | ||
119 | + if(![self messageAvailableDevice]) | ||
120 | + { | ||
121 | + NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying..."); | ||
122 | + [NSThread sleepForTimeInterval:SLEEP_MS]; | ||
123 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableDevice" onSuccess:^(id readValue) | ||
124 | + { | ||
125 | + [self setMessageAvailableDevice:[readValue intValue]]; | ||
126 | + if(![self messageAvailableDevice]) | ||
127 | + { | ||
128 | + NSLog(@"[CommunicationProtocol.m]: Device not ready."); | ||
129 | + NSLog(@"[CommunicationProtocol.m]: Timeout while expecting message"); | ||
130 | + } | ||
131 | + else | ||
132 | + { | ||
133 | + if(self.numPackets == -1) | ||
134 | + { | ||
135 | + [self readNumPackets]; | ||
136 | + } | ||
137 | + else | ||
138 | + { | ||
139 | + [self readNextFragment]; | ||
140 | + } | ||
141 | + } | ||
142 | + }onFail:^(NSError *error) | ||
143 | + { | ||
144 | + NSLog(@"[CommunicationProtocol.m]: On waitForMessage: %@",error); | ||
145 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
146 | + }]; | ||
147 | + } | ||
148 | + else | ||
149 | + { | ||
150 | + if(self.numPackets == -1) | ||
151 | + { | ||
152 | + [self readNumPackets]; | ||
153 | + } | ||
154 | + else | ||
155 | + { | ||
156 | + [self readNextFragment]; | ||
157 | + } | ||
158 | + } | ||
159 | + }onFail:^(NSError *error) | ||
160 | + { | ||
161 | + NSLog(@"[CommunicationProtocol.m]: On waitForMessage: %@",error); | ||
162 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
163 | + }]; | ||
124 | } | 164 | } |
125 | - onFail:^(NSError *error) | 165 | + else |
126 | { | 166 | { |
127 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
128 | - numPackets = 0; | ||
129 | - }]; | ||
130 | - if(numPackets) | ||
131 | - { | ||
132 | - for(int i = 0; i < numPackets; i++) | ||
133 | - { | ||
134 | - //if([self waitForMessageAvailableDevice:TRUE]) | ||
135 | - if(1)//HACK! | ||
136 | - { | ||
137 | - [[EMConnectionManager sharedManager] readResource:@"numBytes" onSuccess:^(id readValue) | ||
138 | - { | ||
139 | - numBytes = (uint8_t) [readValue unsignedCharValue]; | ||
140 | - NSLog(@"[CommunicationProtocol.m]: numBytes read: %d",numBytes); | ||
141 | - } | ||
142 | - onFail:^(NSError *error) | ||
143 | - { | ||
144 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
145 | - numBytes = 0; | ||
146 | - }]; | ||
147 | - if(numBytes) | ||
148 | - { | ||
149 | - [[EMConnectionManager sharedManager] readResource:@"data" onSuccess:^(id readValue) | ||
150 | - { | ||
151 | - if([readValue length] < numBytes) | ||
152 | - { | ||
153 | - NSLog(@"[CommunicationProtocol.m]: WARNING: Device issued wrong numBytes, possible truncated message."); | ||
154 | - } | ||
155 | - [message appendString:[readValue substringToIndex:numBytes]]; | ||
156 | - NSLog(@"[CommunicationProtocol.m]: data read: %@",message); | ||
157 | - } | ||
158 | - onFail:^(NSError *error) | ||
159 | - { | ||
160 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
161 | - }]; | ||
162 | - } | ||
163 | - [[EMConnectionManager sharedManager] writeValue:@"0" toResource:@"messageAvailableDevice" onSuccess:^ | ||
164 | - { | ||
165 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableDevice set to FALSE"); | ||
166 | - NSLog(@"[CommunicationProtocol.m]: packet read"); | ||
167 | - } | ||
168 | - onFail:^(NSError *error) | ||
169 | - { | ||
170 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
171 | - } | ||
172 | - ]; | ||
173 | - } | ||
174 | - else | ||
175 | - { | ||
176 | - NSLog(@"[CommunicationProtocol.m]: Error, resetting message"); | ||
177 | - [message setString:@""]; | ||
178 | - } | ||
179 | - } | ||
180 | - } | ||
181 | - } | ||
182 | - NSLog(@"[CommunicationProtocol.m]: Message received: %@",message); | ||
183 | - return message; | 167 | + if(self.numPackets == -1) |
168 | + { | ||
169 | + [self readNumPackets]; | ||
170 | + } | ||
171 | + else | ||
172 | + { | ||
173 | + [self readNextFragment]; | ||
174 | + } | ||
175 | + } | ||
176 | + }onFail:^(NSError *error) | ||
177 | + { | ||
178 | + NSLog(@"[CommunicationProtocol.m]: On waitForMessage: %@",error); | ||
179 | + [self reportError:[NSString stringWithFormat:@"Error occurred while reading answer from device: %@",[error localizedDescription]]]; | ||
180 | + }]; | ||
184 | } | 181 | } |
185 | 182 | ||
186 | --(Boolean) writeMessage: (NSString*) message | 183 | +-(void) writeMessage: (NSString*) message |
187 | { | 184 | { |
188 | - unsigned long remainingBytes = [message length]; | ||
189 | - uint8_t numBytes, current_index = 0; | ||
190 | - __block Boolean status = TRUE; //HACK | ||
191 | - __block Boolean blockCompleted = TRUE; //HACK! | ||
192 | - [[EMConnectionManager sharedManager] writeValue:@"0" toResource:@"messageAvailableMobile" onSuccess:^ | ||
193 | - { | ||
194 | - status = TRUE; | ||
195 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile set to FALSE"); | ||
196 | - blockCompleted = TRUE; | ||
197 | - } | ||
198 | - onFail:^(NSError *error) | ||
199 | - { | ||
200 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
201 | - status = FALSE; | ||
202 | - blockCompleted = TRUE; | ||
203 | - }]; | ||
204 | - while(blockCompleted != TRUE) | ||
205 | - { | ||
206 | - [NSThread sleepForTimeInterval:1]; | ||
207 | - } | ||
208 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile write block completed"); | ||
209 | - blockCompleted = FALSE; | 185 | + NSLog(@"[CommunicationProtocol.m]: Sending message: %@",message); |
186 | + [self setMessage:[NSMutableString stringWithString:message]]; | ||
187 | + [self setRemainingBytes:[message length]]; | ||
188 | + [self setCurrentIndex:0]; | ||
210 | [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)([message length]/MAX_STRING_LENGTH)+1] toResource:@"numPackets" onSuccess:^ | 189 | [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)([message length]/MAX_STRING_LENGTH)+1] toResource:@"numPackets" onSuccess:^ |
211 | { | 190 | { |
212 | - status = TRUE; | ||
213 | NSLog(@"[CommunicationProtocol.m]: numPackets set to %u",([message length]/MAX_STRING_LENGTH) + 1); | 191 | NSLog(@"[CommunicationProtocol.m]: numPackets set to %u",([message length]/MAX_STRING_LENGTH) + 1); |
214 | - } | ||
215 | - onFail:^(NSError *error) | 192 | + [self sendNextFragment]; |
193 | + }onFail:^(NSError *error) | ||
216 | { | 194 | { |
217 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
218 | - status = FALSE; | ||
219 | - } | ||
220 | - ]; | ||
221 | - NSLog(@"[CommunicationProtocol.m]: status is: %d",status); | ||
222 | - if(status != FALSE) | 195 | + NSLog(@"[CommunicationProtocol.m]: On setNumPackets: %@",error); |
196 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
197 | + }]; | ||
198 | +} | ||
199 | + | ||
200 | +- (void) sendNextFragment | ||
201 | +{ | ||
202 | + NSLog(@"[CommunicationProtocol.m]: Sending next fragment"); | ||
203 | + [[EMConnectionManager sharedManager] writeValue:@"0" toResource:@"messageAvailableMobile" onSuccess:^ | ||
223 | { | 204 | { |
224 | - NSLog(@"[CommunicationProtocol.m]: Carrying on after numPackets..."); | ||
225 | - while(remainingBytes) | 205 | + NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile set to FALSE"); |
206 | + NSUInteger numBytes; | ||
207 | + if(self.remainingBytes) | ||
226 | { | 208 | { |
227 | - if(![self waitForMessageAvailableMobile:FALSE]) | ||
228 | - { | ||
229 | - status = FALSE; | ||
230 | - break; | ||
231 | - } | ||
232 | - NSLog(@"[CommunicationProtocol.m]: Device is ready for next packet"); | ||
233 | - if(remainingBytes > MAX_STRING_LENGTH) | 209 | + if(self.remainingBytes > MAX_STRING_LENGTH) |
234 | { | 210 | { |
235 | numBytes = MAX_STRING_LENGTH; | 211 | numBytes = MAX_STRING_LENGTH; |
236 | - remainingBytes -= MAX_STRING_LENGTH; | 212 | + self.remainingBytes -= MAX_STRING_LENGTH; |
237 | } | 213 | } |
238 | else | 214 | else |
239 | { | 215 | { |
240 | - numBytes = remainingBytes; | ||
241 | - remainingBytes = 0; | 216 | + numBytes = self.remainingBytes; |
217 | + self.remainingBytes = 0; | ||
242 | } | 218 | } |
243 | [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)numBytes] toResource:@"numBytes" onSuccess:^ | 219 | [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)numBytes] toResource:@"numBytes" onSuccess:^ |
244 | { | 220 | { |
245 | NSLog(@"[CommunicationProtocol.m]: numBytes set to %d", numBytes); | 221 | NSLog(@"[CommunicationProtocol.m]: numBytes set to %d", numBytes); |
246 | - status = TRUE; | ||
247 | - } | ||
248 | - onFail:^(NSError *error) | 222 | + NSString *data = [self.message substringWithRange:NSMakeRange(self.currentIndex, numBytes)]; |
223 | + self.currentIndex += numBytes; | ||
224 | + [[EMConnectionManager sharedManager] writeValue:data toResource:@"data" onSuccess:^ | ||
225 | + { | ||
226 | + NSLog(@"[CommunicationProtocol.m]: data set to: %@",data); | ||
227 | + [[EMConnectionManager sharedManager] writeValue:@"1" toResource:@"messageAvailableMobile" onSuccess:^ | ||
228 | + { | ||
229 | + NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile set to TRUE"); | ||
230 | + NSLog(@"[CommunicationProtocol.m]: Packet written"); | ||
231 | + [self readACK]; | ||
232 | + }onFail:^(NSError *error) | ||
233 | + { | ||
234 | + NSLog(@"[CommunicationProtocol.m]: On setMessageAvailable to TRUE: %@",error); | ||
235 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
236 | + }]; | ||
237 | + }onFail:^(NSError *error) | ||
238 | + { | ||
239 | + NSLog(@"[CommunicationProtocol.m]: On setData: %@",error); | ||
240 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
241 | + }]; | ||
242 | + }onFail:^(NSError *error) | ||
249 | { | 243 | { |
250 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
251 | - status = FALSE; | ||
252 | - } | ||
253 | - ]; | ||
254 | - if(status != FALSE) | 244 | + NSLog(@"[CommunicationProtocol.m]: On setNumBytes: %@",error); |
245 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
246 | + }]; | ||
247 | + } | ||
248 | + else | ||
249 | + { | ||
250 | + [self readMessage]; | ||
251 | + } | ||
252 | + | ||
253 | + }onFail:^(NSError *error) | ||
254 | + { | ||
255 | + NSLog(@"[CommunicationProtocol.m]: On setMessageAvailableMobile to FALSE: %@",error); | ||
256 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
257 | + }]; | ||
258 | +} | ||
259 | + | ||
260 | +- (void) readACK | ||
261 | +{ | ||
262 | + NSLog(@"[CommunicationProtocol.m]: Reading ACK from device"); | ||
263 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableMobile" onSuccess:^(id readValue) | ||
264 | + { | ||
265 | + [self setMessageAvailableMobile:[readValue intValue]]; | ||
266 | + if([self messageAvailableMobile]) | ||
267 | + { | ||
268 | + NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying..."); | ||
269 | + [NSThread sleepForTimeInterval:SLEEP_MS]; | ||
270 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableMobile" onSuccess:^(id readValue) | ||
255 | { | 271 | { |
256 | - NSLog(@"[CommunicationProtocol.m]: Carrying on after numBytes..."); | ||
257 | - NSString *data = [message substringWithRange:NSMakeRange(current_index, numBytes)]; | ||
258 | - current_index += numBytes; | ||
259 | - [[EMConnectionManager sharedManager] writeValue:data toResource:@"data" onSuccess:^ | ||
260 | - { | ||
261 | - NSLog(@"[CommunicationProtocol.m]: data set to: %@",data); | ||
262 | - status = TRUE; | ||
263 | - } | ||
264 | - onFail:^(NSError *error) | ||
265 | - { | ||
266 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
267 | - status = FALSE; | ||
268 | - } | ||
269 | - ]; | ||
270 | - if(status != FALSE) | 272 | + [self setMessageAvailableMobile:[readValue intValue]]; |
273 | + if([self messageAvailableMobile]) | ||
271 | { | 274 | { |
272 | - NSLog(@"[CommunicationProtocol.m]: Carrying on after data..."); | ||
273 | - [[EMConnectionManager sharedManager] writeValue:@"1" toResource:@"messageAvailableMobile" onSuccess:^ | ||
274 | - { | ||
275 | - status = TRUE; | ||
276 | - NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile set to TRUE"); | ||
277 | - NSLog(@"[CommunicationProtocol.m]: Packet written"); | ||
278 | - } | ||
279 | - onFail:^(NSError *error) | ||
280 | - { | ||
281 | - NSLog(@"[CommunicationProtocol.m]: %@",error); | ||
282 | - status = FALSE; | ||
283 | - } | ||
284 | - ]; | ||
285 | - NSLog(@"[CommunicationProtocol.m]: Carrying on after messageAvailableMobile..."); | 275 | + NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying..."); |
276 | + [NSThread sleepForTimeInterval:SLEEP_MS]; | ||
277 | + [[EMConnectionManager sharedManager] readResource:@"messageAvailableMobile" onSuccess:^(id readValue) | ||
278 | + { | ||
279 | + [self setMessageAvailableMobile:[readValue intValue]]; | ||
280 | + if([self messageAvailableMobile]) | ||
281 | + { | ||
282 | + NSLog(@"[CommunicationProtocol.m]: Device not ready."); | ||
283 | + NSLog(@"[CommunicationProtocol.m]: Timeout while expecting ACK"); | ||
284 | + } | ||
285 | + else | ||
286 | + { | ||
287 | + [self sendNextFragment]; | ||
288 | + } | ||
289 | + }onFail:^(NSError *error) | ||
290 | + { | ||
291 | + NSLog(@"[CommunicationProtocol.m]: On readACK: %@",error); | ||
292 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
293 | + }]; | ||
286 | } | 294 | } |
287 | - } | 295 | + else |
296 | + { | ||
297 | + [self sendNextFragment]; | ||
298 | + } | ||
299 | + }onFail:^(NSError *error) | ||
300 | + { | ||
301 | + NSLog(@"[CommunicationProtocol.m]: On readACK: %@",error); | ||
302 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
303 | + }]; | ||
288 | } | 304 | } |
289 | - } | ||
290 | - if(![self waitForMessageAvailableMobile:FALSE]) | 305 | + else |
306 | + { | ||
307 | + [self sendNextFragment]; | ||
308 | + } | ||
309 | + }onFail:^(NSError *error) | ||
291 | { | 310 | { |
292 | - NSLog(@"[CommunicationProtocol.m]: Device has processed the message"); | ||
293 | - } | ||
294 | - return status; | 311 | + NSLog(@"[CommunicationProtocol.m]: On readACK: %@",error); |
312 | + [self reportError:[NSString stringWithFormat:@"Error occurred while sending command to device: %@",[error localizedDescription]]]; | ||
313 | + }]; | ||
295 | } | 314 | } |
296 | 315 | ||
297 | -(Boolean) establishConnection | 316 | -(Boolean) establishConnection |
DUREX Vendor Control/DUREX Vendor Control.xcodeproj/project.xcworkspace/xcuserdata/imanol.xcuserdatad/UserInterfaceState.xcuserstate
No preview for this file type
DUREX Vendor Control/DUREX Vendor Control.xcodeproj/xcuserdata/imanol.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -74,12 +74,12 @@ | @@ -74,12 +74,12 @@ | ||
74 | ignoreCount = "0" | 74 | ignoreCount = "0" |
75 | continueAfterRunningActions = "No" | 75 | continueAfterRunningActions = "No" |
76 | filePath = "CommunicationProtocol.m" | 76 | filePath = "CommunicationProtocol.m" |
77 | - timestampString = "432263020.427289" | 77 | + timestampString = "433360607.314289" |
78 | startingColumnNumber = "9223372036854775807" | 78 | startingColumnNumber = "9223372036854775807" |
79 | endingColumnNumber = "9223372036854775807" | 79 | endingColumnNumber = "9223372036854775807" |
80 | - startingLineNumber = "362" | ||
81 | - endingLineNumber = "362" | ||
82 | - landmarkName = "-readSensorData" | 80 | + startingLineNumber = "363" |
81 | + endingLineNumber = "363" | ||
82 | + landmarkName = "-readNextFragment" | ||
83 | landmarkType = "5"> | 83 | landmarkType = "5"> |
84 | </BreakpointContent> | 84 | </BreakpointContent> |
85 | </BreakpointProxy> | 85 | </BreakpointProxy> |
@@ -90,12 +90,12 @@ | @@ -90,12 +90,12 @@ | ||
90 | ignoreCount = "0" | 90 | ignoreCount = "0" |
91 | continueAfterRunningActions = "No" | 91 | continueAfterRunningActions = "No" |
92 | filePath = "CommunicationProtocol.m" | 92 | filePath = "CommunicationProtocol.m" |
93 | - timestampString = "432263020.427289" | 93 | + timestampString = "433360607.314289" |
94 | startingColumnNumber = "9223372036854775807" | 94 | startingColumnNumber = "9223372036854775807" |
95 | endingColumnNumber = "9223372036854775807" | 95 | endingColumnNumber = "9223372036854775807" |
96 | - startingLineNumber = "361" | ||
97 | - endingLineNumber = "361" | ||
98 | - landmarkName = "-readSensorData" | 96 | + startingLineNumber = "362" |
97 | + endingLineNumber = "362" | ||
98 | + landmarkName = "-readNextFragment" | ||
99 | landmarkType = "5"> | 99 | landmarkType = "5"> |
100 | </BreakpointContent> | 100 | </BreakpointContent> |
101 | </BreakpointProxy> | 101 | </BreakpointProxy> |
@@ -106,12 +106,12 @@ | @@ -106,12 +106,12 @@ | ||
106 | ignoreCount = "0" | 106 | ignoreCount = "0" |
107 | continueAfterRunningActions = "No" | 107 | continueAfterRunningActions = "No" |
108 | filePath = "CommunicationProtocol.m" | 108 | filePath = "CommunicationProtocol.m" |
109 | - timestampString = "432263020.427289" | 109 | + timestampString = "433360607.314289" |
110 | startingColumnNumber = "9223372036854775807" | 110 | startingColumnNumber = "9223372036854775807" |
111 | endingColumnNumber = "9223372036854775807" | 111 | endingColumnNumber = "9223372036854775807" |
112 | - startingLineNumber = "380" | ||
113 | - endingLineNumber = "380" | ||
114 | - landmarkName = "-readSensorData" | 112 | + startingLineNumber = "381" |
113 | + endingLineNumber = "381" | ||
114 | + landmarkName = "-readNextFragment" | ||
115 | landmarkType = "5"> | 115 | landmarkType = "5"> |
116 | </BreakpointContent> | 116 | </BreakpointContent> |
117 | </BreakpointProxy> | 117 | </BreakpointProxy> |
@@ -201,22 +201,6 @@ | @@ -201,22 +201,6 @@ | ||
201 | shouldBeEnabled = "No" | 201 | shouldBeEnabled = "No" |
202 | ignoreCount = "0" | 202 | ignoreCount = "0" |
203 | continueAfterRunningActions = "No" | 203 | continueAfterRunningActions = "No" |
204 | - filePath = "CommunicationProtocol.m" | ||
205 | - timestampString = "432263020.427289" | ||
206 | - startingColumnNumber = "9223372036854775807" | ||
207 | - endingColumnNumber = "9223372036854775807" | ||
208 | - startingLineNumber = "213" | ||
209 | - endingLineNumber = "213" | ||
210 | - landmarkName = "-writeMessage:" | ||
211 | - landmarkType = "5"> | ||
212 | - </BreakpointContent> | ||
213 | - </BreakpointProxy> | ||
214 | - <BreakpointProxy | ||
215 | - BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
216 | - <BreakpointContent | ||
217 | - shouldBeEnabled = "No" | ||
218 | - ignoreCount = "0" | ||
219 | - continueAfterRunningActions = "No" | ||
220 | filePath = "MenuTableViewController.m" | 204 | filePath = "MenuTableViewController.m" |
221 | timestampString = "432263020.427289" | 205 | timestampString = "432263020.427289" |
222 | startingColumnNumber = "9223372036854775807" | 206 | startingColumnNumber = "9223372036854775807" |
@@ -522,11 +506,11 @@ | @@ -522,11 +506,11 @@ | ||
522 | ignoreCount = "0" | 506 | ignoreCount = "0" |
523 | continueAfterRunningActions = "No" | 507 | continueAfterRunningActions = "No" |
524 | filePath = "DateRangePickerViewController.m" | 508 | filePath = "DateRangePickerViewController.m" |
525 | - timestampString = "432833512.682389" | 509 | + timestampString = "433360688.202825" |
526 | startingColumnNumber = "9223372036854775807" | 510 | startingColumnNumber = "9223372036854775807" |
527 | endingColumnNumber = "9223372036854775807" | 511 | endingColumnNumber = "9223372036854775807" |
528 | - startingLineNumber = "131" | ||
529 | - endingLineNumber = "131" | 512 | + startingLineNumber = "133" |
513 | + endingLineNumber = "133" | ||
530 | landmarkName = "-toggleFromDate:" | 514 | landmarkName = "-toggleFromDate:" |
531 | landmarkType = "5"> | 515 | landmarkType = "5"> |
532 | </BreakpointContent> | 516 | </BreakpointContent> |
@@ -538,11 +522,11 @@ | @@ -538,11 +522,11 @@ | ||
538 | ignoreCount = "0" | 522 | ignoreCount = "0" |
539 | continueAfterRunningActions = "No" | 523 | continueAfterRunningActions = "No" |
540 | filePath = "DateRangePickerViewController.m" | 524 | filePath = "DateRangePickerViewController.m" |
541 | - timestampString = "432833512.682389" | 525 | + timestampString = "433360688.202825" |
542 | startingColumnNumber = "9223372036854775807" | 526 | startingColumnNumber = "9223372036854775807" |
543 | endingColumnNumber = "9223372036854775807" | 527 | endingColumnNumber = "9223372036854775807" |
544 | - startingLineNumber = "155" | ||
545 | - endingLineNumber = "155" | 528 | + startingLineNumber = "159" |
529 | + endingLineNumber = "159" | ||
546 | landmarkName = "-toggleToDate:" | 530 | landmarkName = "-toggleToDate:" |
547 | landmarkType = "5"> | 531 | landmarkType = "5"> |
548 | </BreakpointContent> | 532 | </BreakpointContent> |
@@ -554,11 +538,11 @@ | @@ -554,11 +538,11 @@ | ||
554 | ignoreCount = "0" | 538 | ignoreCount = "0" |
555 | continueAfterRunningActions = "No" | 539 | continueAfterRunningActions = "No" |
556 | filePath = "DateRangePickerViewController.m" | 540 | filePath = "DateRangePickerViewController.m" |
557 | - timestampString = "432833512.682389" | 541 | + timestampString = "433360688.202825" |
558 | startingColumnNumber = "9223372036854775807" | 542 | startingColumnNumber = "9223372036854775807" |
559 | endingColumnNumber = "9223372036854775807" | 543 | endingColumnNumber = "9223372036854775807" |
560 | - startingLineNumber = "154" | ||
561 | - endingLineNumber = "154" | 544 | + startingLineNumber = "158" |
545 | + endingLineNumber = "158" | ||
562 | landmarkName = "-toggleToDate:" | 546 | landmarkName = "-toggleToDate:" |
563 | landmarkType = "5"> | 547 | landmarkType = "5"> |
564 | </BreakpointContent> | 548 | </BreakpointContent> |
@@ -602,14 +586,28 @@ | @@ -602,14 +586,28 @@ | ||
602 | ignoreCount = "0" | 586 | ignoreCount = "0" |
603 | continueAfterRunningActions = "No" | 587 | continueAfterRunningActions = "No" |
604 | filePath = "DateRangePickerViewController.m" | 588 | filePath = "DateRangePickerViewController.m" |
605 | - timestampString = "432833363.738094" | 589 | + timestampString = "433360688.202825" |
606 | startingColumnNumber = "9223372036854775807" | 590 | startingColumnNumber = "9223372036854775807" |
607 | endingColumnNumber = "9223372036854775807" | 591 | endingColumnNumber = "9223372036854775807" |
608 | - startingLineNumber = "102" | ||
609 | - endingLineNumber = "102" | ||
610 | - landmarkName = "-resizeViewHeight::" | 592 | + startingLineNumber = "141" |
593 | + endingLineNumber = "141" | ||
594 | + landmarkName = "-toggleFromDate:" | ||
611 | landmarkType = "5"> | 595 | landmarkType = "5"> |
612 | </BreakpointContent> | 596 | </BreakpointContent> |
613 | </BreakpointProxy> | 597 | </BreakpointProxy> |
598 | + <BreakpointProxy | ||
599 | + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
600 | + <BreakpointContent | ||
601 | + shouldBeEnabled = "No" | ||
602 | + ignoreCount = "0" | ||
603 | + continueAfterRunningActions = "No" | ||
604 | + filePath = "CommunicationProtocol.m" | ||
605 | + timestampString = "433434179.370975" | ||
606 | + startingColumnNumber = "9223372036854775807" | ||
607 | + endingColumnNumber = "9223372036854775807" | ||
608 | + startingLineNumber = "21" | ||
609 | + endingLineNumber = "21"> | ||
610 | + </BreakpointContent> | ||
611 | + </BreakpointProxy> | ||
614 | </Breakpoints> | 612 | </Breakpoints> |
615 | </Bucket> | 613 | </Bucket> |
DUREX Vendor Control/DateRangePickerViewController.m
@@ -11,6 +11,7 @@ | @@ -11,6 +11,7 @@ | ||
11 | @interface DateRangePickerViewController () | 11 | @interface DateRangePickerViewController () |
12 | 12 | ||
13 | @property uint8_t isExtended; | 13 | @property uint8_t isExtended; |
14 | +@property NSUInteger currentHeight; | ||
14 | 15 | ||
15 | @end | 16 | @end |
16 | 17 | ||
@@ -40,6 +41,8 @@ | @@ -40,6 +41,8 @@ | ||
40 | 41 | ||
41 | [self setIsExtended:0]; | 42 | [self setIsExtended:0]; |
42 | 43 | ||
44 | + [self setCurrentHeight:[self scrollView].frame.size.height]; | ||
45 | + | ||
43 | [super viewDidLoad]; | 46 | [super viewDidLoad]; |
44 | // Do any additional setup after loading the view from its nib. | 47 | // Do any additional setup after loading the view from its nib. |
45 | } | 48 | } |
@@ -97,19 +100,19 @@ | @@ -97,19 +100,19 @@ | ||
97 | [UIView commitAnimations]; | 100 | [UIView commitAnimations]; |
98 | } | 101 | } |
99 | 102 | ||
100 | -- (void) resizeViewHeight : (UIView*) view : (NSInteger) height | 103 | +- (void) resizeViewHeight : (UIView*) view : (NSInteger) height : (Boolean) ignoreHeight |
101 | { | 104 | { |
102 | CGRect screenRect = [[UIScreen mainScreen] bounds]; | 105 | CGRect screenRect = [[UIScreen mainScreen] bounds]; |
103 | CGFloat screenHeight = screenRect.size.height; | 106 | CGFloat screenHeight = screenRect.size.height; |
104 | - if((view.frame.size.height + height > screenHeight) && (height > 0)) | 107 | + if((height > screenHeight) && !ignoreHeight) |
105 | { | 108 | { |
106 | NSLog(@"[DateRangePickerViewController.m]: Screen height limit reached"); | 109 | NSLog(@"[DateRangePickerViewController.m]: Screen height limit reached"); |
107 | } | 110 | } |
108 | - else if(((height < 0) && ([self isExtended] != 0)) || ((height > 0) && ([self isExtended] != 2))) | 111 | + else |
109 | { | 112 | { |
110 | [UIView beginAnimations:nil context:NULL]; | 113 | [UIView beginAnimations:nil context:NULL]; |
111 | [UIView setAnimationDuration:0.5]; | 114 | [UIView setAnimationDuration:0.5]; |
112 | - view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width,view.frame.size.height + height); | 115 | + view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width,height); |
113 | [UIView commitAnimations]; | 116 | [UIView commitAnimations]; |
114 | } | 117 | } |
115 | if(height < 0) | 118 | if(height < 0) |
@@ -120,8 +123,7 @@ | @@ -120,8 +123,7 @@ | ||
120 | { | 123 | { |
121 | [self setIsExtended: [self isExtended] + 1]; | 124 | [self setIsExtended: [self isExtended] + 1]; |
122 | } | 125 | } |
123 | - [self.scrollView setContentSize:CGSizeMake([self scrolledView].frame.size.width,[self scrolledView].frame.size.height)]; | ||
124 | - NSLog(@"ScrollView height: %f",[self scrollView].frame.size.height); | 126 | + [self.scrollView setContentSize:CGSizeMake([self scrolledView].frame.size.width,[self currentHeight])]; |
125 | } | 127 | } |
126 | 128 | ||
127 | - (IBAction)toggleFromDate:(id)sender | 129 | - (IBAction)toggleFromDate:(id)sender |
@@ -135,7 +137,8 @@ | @@ -135,7 +137,8 @@ | ||
135 | [self moveDown:[self toDate] :height]; | 137 | [self moveDown:[self toDate] :height]; |
136 | [self moveDown:[self doneButton] :height]; | 138 | [self moveDown:[self doneButton] :height]; |
137 | [self moveDown:[self toLabel] :height]; | 139 | [self moveDown:[self toLabel] :height]; |
138 | - [self resizeViewHeight : [self scrollView] : height + 20]; | 140 | + [self setCurrentHeight:[self currentHeight] + height + 20]; |
141 | + [self resizeViewHeight : [self scrollView] : [self currentHeight] : FALSE]; | ||
139 | } | 142 | } |
140 | else | 143 | else |
141 | { | 144 | { |
@@ -145,7 +148,8 @@ | @@ -145,7 +148,8 @@ | ||
145 | [self moveUp:[self toDate] :height]; | 148 | [self moveUp:[self toDate] :height]; |
146 | [self moveUp:[self doneButton] :height]; | 149 | [self moveUp:[self doneButton] :height]; |
147 | [self moveUp:[self toLabel] :height]; | 150 | [self moveUp:[self toLabel] :height]; |
148 | - [self resizeViewHeight : [self scrollView] : - height - 20]; | 151 | + [self setCurrentHeight:[self currentHeight] - height - 20]; |
152 | + [self resizeViewHeight : [self scrollView] : [self currentHeight] : FALSE]; | ||
149 | } | 153 | } |
150 | } | 154 | } |
151 | - (IBAction)toggleToDate:(id)sender | 155 | - (IBAction)toggleToDate:(id)sender |
@@ -156,14 +160,16 @@ | @@ -156,14 +160,16 @@ | ||
156 | [[self toDate] setEnabled:TRUE]; | 160 | [[self toDate] setEnabled:TRUE]; |
157 | [[self toDate] setHidden:FALSE]; | 161 | [[self toDate] setHidden:FALSE]; |
158 | [self moveDown:[self doneButton] :height]; | 162 | [self moveDown:[self doneButton] :height]; |
159 | - [self resizeViewHeight : [self scrollView] : height + 20]; | 163 | + [self setCurrentHeight:[self currentHeight] + height + 20]; |
164 | + [self resizeViewHeight : [self scrollView] : [self currentHeight] : FALSE]; | ||
160 | } | 165 | } |
161 | else | 166 | else |
162 | { | 167 | { |
163 | [[self toDate] setEnabled:FALSE]; | 168 | [[self toDate] setEnabled:FALSE]; |
164 | [[self toDate] setHidden:TRUE]; | 169 | [[self toDate] setHidden:TRUE]; |
165 | [self moveUp:[self doneButton] :height]; | 170 | [self moveUp:[self doneButton] :height]; |
166 | - [self resizeViewHeight : [self scrollView] : - height - 20]; | 171 | + [self setCurrentHeight:[self currentHeight] - height - 20]; |
172 | + [self resizeViewHeight : [self scrollView] : [self currentHeight] : FALSE]; | ||
167 | } | 173 | } |
168 | } | 174 | } |
169 | 175 |
DUREX Vendor Control/MenuTableViewController.m
@@ -333,7 +333,8 @@ | @@ -333,7 +333,8 @@ | ||
333 | { | 333 | { |
334 | if ([[EMConnectionManager sharedManager] connectionState] == EMConnectionStateDisrupted) | 334 | if ([[EMConnectionManager sharedManager] connectionState] == EMConnectionStateDisrupted) |
335 | { | 335 | { |
336 | - [[self navigationController] popToRootViewControllerAnimated:YES]; | 336 | + UIViewController *previous = [[[self navigationController] viewControllers] objectAtIndex:[[[self navigationController] viewControllers] count]-2]; |
337 | + [[self navigationController] popToViewController:previous animated:YES]; | ||
337 | } | 338 | } |
338 | } | 339 | } |
339 | } | 340 | } |
TODO
1 | +TESTS: | ||
2 | +- Test disconnection | ||
3 | + | ||
1 | BUGS: | 4 | BUGS: |
2 | - On date change, response is overwritten by previous query, trimming needed according to numBytes | 5 | - On date change, response is overwritten by previous query, trimming needed according to numBytes |
3 | 6 | ||
4 | TODO: | 7 | TODO: |
5 | - | ||
6 | -- Reimplement protocol with async pattern | ||
7 | -- Handle disconnects properly | 8 | +- processMessage with Delegate |
9 | +- Error reporting function | ||
8 | - Incident class | 10 | - Incident class |
9 | - Incident parser | 11 | - Incident parser |
10 | - A3 command | 12 | - A3 command |
13 | +- BYE command | ||
11 | - Channels and codes to 2 ciphers | 14 | - Channels and codes to 2 ciphers |
12 | - Add landscape layout and inverted layout | 15 | - Add landscape layout and inverted layout |
13 | - Month/Year headers on sale list | 16 | - Month/Year headers on sale list |