SELKIELogger  1.0.0
LPMSConnection.c
1 /*
2  * Copyright (C) 2023 Swansea University
3  *
4  * This file is part of the SELKIELogger suite of tools.
5  *
6  * SELKIELogger is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * SELKIELogger is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this SELKIELogger product.
18  * If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <errno.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "SELKIELoggerBase.h"
28 
29 #include "LPMSConnection.h"
30 
31 #include "LPMSMessages.h"
32 
40 int lpms_openConnection(const char *device, const int baud) {
41  if (device == NULL) { return -1; }
42  return openSerialConnection(device, baud);
43 }
44 
48 void lpms_closeConnection(int handle) {
49  errno = 0;
50  close(handle);
51 }
52 
63 bool lpms_readMessage(int handle, lpms_message *out) {
64  static uint8_t buf[LPMS_BUFF];
65  static size_t index = 0; // Current read position
66  static size_t hw = 0; // Current array end
67  return lpms_readMessage_buf(handle, out, buf, &index, &hw);
68 }
69 
87 bool lpms_readMessage_buf(int handle, lpms_message *out, uint8_t buf[LPMS_BUFF], size_t *index, size_t *hw) {
88  int ti = 0;
89  if ((*hw) < LPMS_BUFF - 1) {
90  errno = 0;
91  ti = read(handle, &(buf[(*hw)]), LPMS_BUFF - (*hw));
92  if (ti >= 0) {
93  (*hw) += ti;
94  } else {
95  if (errno != EAGAIN) {
96  fprintf(stderr, "Unexpected error while reading from serial port (handle ID: 0x%02x)\n",
97  handle);
98  fprintf(stderr, "read returned \"%s\" in readMessage\n", strerror(errno));
99  out->id = 0xAA;
100  return false;
101  }
102  }
103  }
104 
105  if (((*hw) == LPMS_BUFF) && (*index) > 0 && (*index) > ((*hw) - 25)) {
106  // Full buffer, very close to the fill limit
107  // Assume we're full of garbage before index
108  memmove(buf, &(buf[(*index)]), LPMS_BUFF - (*index));
109  (*hw) -= (*index);
110  (*index) = 0;
111  out->id = 0xFF;
112  if (ti == 0) { out->id = 0xFD; }
113  return false;
114  }
115 
116  lpms_message t = {0};
117  bool r = lpms_from_bytes(buf, *hw, &t, index);
118  if (r) {
119  (*out) = t;
120  } else {
121  if (ti == 0 && ((*hw) - (*index)) < 10) {
122  // No data available and less than 1 message in buffer
123  out->id = 0xFD;
124  } else {
125  if ((*index) < (*hw)) { (*index)++; }
126  }
127  if (t.data) { free(t.data); } // Not passing message back
128  }
129 
130  if ((*hw) > 0 && ((*hw) >= (*index))) {
131  // Move data from index back to zero position
132  memmove(buf, &(buf[(*index)]), LPMS_BUFF - (*index));
133  (*hw) -= (*index);
134  (*index) = 0;
135  memset(&(buf[(*hw)]), 0, LPMS_BUFF - (*hw));
136  }
137  return r;
138 }
139 
157 bool lpms_find_messages(int handle, size_t numtypes, const uint8_t types[], int timeout, lpms_message *out,
158  uint8_t buf[LPMS_BUFF], size_t *index, size_t *hw) {
159  time_t start = time(NULL);
160  while (time(NULL) <= (start + timeout)) {
161  lpms_message t = {0};
162  bool rs = lpms_readMessage_buf(handle, &t, buf, index, hw);
163  if (rs) {
164  fprintf(stdout, "%02x: Command 0x%02x received while waiting\n", t.id, t.command);
165  for (unsigned int c = 0; c < numtypes; c++) {
166  if (t.command == types[c]) {
167  if (out) { (*out) = t; }
168  return true;
169  }
170  }
171  }
172  if (t.data) { free(t.data); }
173  }
174 
175  return false;
176 }
177 
178 bool lpms_send_command(const int handle, lpms_message *m) {
179  uint16_t cs = 0;
180  // Ignore the return value as we're resetting the checksum anyway
181  (void)lpms_checksum(m, &cs);
182  m->checksum = cs;
183  uint8_t *arr = NULL;
184  size_t len = 0;
185  bool rs = lpms_to_bytes(m, &arr, &len);
186  if (!rs) { return false; }
187  rs = (write(handle, arr, len) == (ssize_t) len);
188  free(arr);
189  return rs;
190 }
191 
192 bool lpms_send_command_mode(const int handle) {
193  lpms_message m = {.id = 1, .command = 0x0006};
194  return lpms_send_command(handle, &m);
195 }
196 
197 bool lpms_send_stream_mode(const int handle) {
198  lpms_message m = {.id = 1, .command = 0x0007};
199  return lpms_send_command(handle, &m);
200 }
201 
202 bool lpms_send_get_config(const int handle) {
203  lpms_message m = {.id = 1, .command = 0x0004};
204  return lpms_send_command(handle, &m);
205 }
bool lpms_readMessage_buf(int handle, lpms_message *out, uint8_t buf[LPMS_BUFF], size_t *index, size_t *hw)
Read data from handle, and parse message if able.
bool lpms_send_command(const int handle, lpms_message *m)
Write command defined by structure to handle.
bool lpms_find_messages(int handle, size_t numtypes, const uint8_t types[], int timeout, lpms_message *out, uint8_t buf[LPMS_BUFF], size_t *index, size_t *hw)
Read data from handle until first of specified message types is found.
#define LPMS_BUFF
Default serial buffer allocation size.
bool lpms_readMessage(int handle, lpms_message *out)
Static wrapper around lpms_readMessage_buf.
bool lpms_send_get_config(const int handle)
Shortcut: Send LPMS_MSG_GET_OUTPUTS.
void lpms_closeConnection(int handle)
Close LPMS serial connection.
int lpms_openConnection(const char *device, const int baud)
Open connection to an LPMS serial device.
bool lpms_send_stream_mode(const int handle)
Shortcut: Send LPMS_MSG_MODE_STREAM.
bool lpms_send_command_mode(const int handle)
Shortcut: Send LPMS_MSG_MODE_CMD.
bool lpms_checksum(const lpms_message *msg, uint16_t *csum)
Calculate checksum for LPMS message packet.
Definition: LPMSMessages.c:135
bool lpms_to_bytes(const lpms_message *msg, uint8_t **out, size_t *len)
Convert message structure to flat array.
Definition: LPMSMessages.c:104
bool lpms_from_bytes(const uint8_t *in, const size_t len, lpms_message *msg, size_t *pos)
Read bytes and populate message structure.
Definition: LPMSMessages.c:42
int openSerialConnection(const char *port, const int baudRate)
Open a serial connection at a given baud rate.
Definition: serial.c:44
Represent LPMS message.
Definition: LPMSTypes.h:48
uint16_t checksum
Sum of all preceding message bytes.
Definition: LPMSTypes.h:52
uint8_t * data
Pointer to data array.
Definition: LPMSTypes.h:53
uint16_t id
Source/Destination Sensor ID.
Definition: LPMSTypes.h:49
uint16_t command
Message type.
Definition: LPMSTypes.h:50