-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathisobus_time_date_interface.cpp
217 lines (194 loc) · 10.9 KB
/
isobus_time_date_interface.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//================================================================================================
/// @file isobus_time_date_interface.cpp
///
/// @brief Implements an interface to handle to transmit the time and date information using the
/// Time/Date (TD) PGN.
///
/// @author Adrian Del Grosso
///
/// @copyright 2024 The Open-Agriculture Developers
//================================================================================================
#include "isobus_time_date_interface.hpp"
#include "can_general_parameter_group_numbers.hpp"
#include "can_parameter_group_number_request_protocol.hpp"
#include "can_stack_logger.hpp"
#include "to_string.hpp"
#include <cassert>
#ifndef DISABLE_CAN_STACK_LOGGER
#include <iomanip>
#include <sstream>
#endif
namespace isobus
{
TimeDateInterface::TimeDateInterface(std::shared_ptr<InternalControlFunction> sourceControlFunction, std::function<bool(TimeAndDate &timeAndDateToPopulate)> timeAndDateCallback) :
myControlFunction(sourceControlFunction),
userTimeDateCallback(timeAndDateCallback)
{
if (nullptr != sourceControlFunction)
{
assert(nullptr != timeAndDateCallback); // You need a callback to populate the time and date information... the interface needs to know the time and date to send it out on the bus!
}
}
TimeDateInterface::~TimeDateInterface()
{
if (initialized && (nullptr != myControlFunction))
{
auto pgnRequestProtocol = myControlFunction->get_pgn_request_protocol().lock();
if (nullptr != pgnRequestProtocol)
{
pgnRequestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate), process_request_for_time_date, this);
}
}
}
void TimeDateInterface::initialize()
{
if (!initialized)
{
CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate),
process_rx_message,
this);
if (nullptr != myControlFunction)
{
auto pgnRequestProtocol = myControlFunction->get_pgn_request_protocol().lock();
if (nullptr != pgnRequestProtocol)
{
pgnRequestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate), process_request_for_time_date, this);
}
}
initialized = true;
}
}
bool TimeDateInterface::is_initialized() const
{
return initialized;
}
EventDispatcher<TimeDateInterface::TimeAndDateInformation> &TimeDateInterface::get_event_dispatcher()
{
return timeAndDateEventDispatcher;
}
bool TimeDateInterface::send_time_and_date(const TimeAndDate &timeAndDateToSend) const
{
// If you hit any of these assertions, it's because you are trying to send an invalid time and date.
// Sending invalid values on the network is bad.
// Please check the values you are trying to send and make sure they are within the valid ranges noted below.
// These values can also be found in the ISO11783-7 standard (on isobus.net), or in the J1939 standard.
// Also, please only send the time and date if you have a good RTC or GPS source. Sending bad time and date information can cause issues for other devices on the network.
assert(timeAndDateToSend.year >= 1985 && timeAndDateToSend.year <= 2235); // The year must be between 1985 and 2235
assert(timeAndDateToSend.month >= 1 && timeAndDateToSend.month <= 12); // The month must be between 1 and 12
assert(timeAndDateToSend.day <= 31); // The day must be between 0 and 31
assert(timeAndDateToSend.hours <= 23); // The hours must be between 0 and 23
assert(timeAndDateToSend.minutes <= 59); // The minutes must be between 0 and 59
assert(timeAndDateToSend.seconds <= 59); // The seconds must be between 0 and 59
assert(timeAndDateToSend.quarterDays <= 3); // The quarter days must be between 0 and 3
assert(timeAndDateToSend.milliseconds == 0 || timeAndDateToSend.milliseconds == 250 || timeAndDateToSend.milliseconds == 500 || timeAndDateToSend.milliseconds == 750); // The milliseconds must be 0, 250, 500, or 750
assert(timeAndDateToSend.localHourOffset >= -23 && timeAndDateToSend.localHourOffset <= 23); // The local hour offset must be between -23 and 23
assert(timeAndDateToSend.localMinuteOffset >= -59 && timeAndDateToSend.localMinuteOffset <= 59); // The local minute offset must be between -59 and 59
const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = {
static_cast<std::uint8_t>(timeAndDateToSend.seconds * 4 + (timeAndDateToSend.milliseconds / 250)),
timeAndDateToSend.minutes,
timeAndDateToSend.hours,
timeAndDateToSend.month,
static_cast<std::uint8_t>(timeAndDateToSend.day * 4 + timeAndDateToSend.quarterDays),
static_cast<std::uint8_t>(timeAndDateToSend.year - 1985),
static_cast<std::uint8_t>(timeAndDateToSend.localMinuteOffset + 125),
static_cast<std::uint8_t>(timeAndDateToSend.localHourOffset + 125)
};
return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate),
buffer.data(),
buffer.size(),
myControlFunction,
nullptr,
CANIdentifier::CANPriority::PriorityDefault6);
}
bool TimeDateInterface::request_time_and_date(std::shared_ptr<InternalControlFunction> requestingControlFunction, std::shared_ptr<ControlFunction> optionalDestination) const
{
bool retVal = false;
if (nullptr != requestingControlFunction)
{
retVal = ParameterGroupNumberRequestProtocol::request_parameter_group_number(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate),
requestingControlFunction,
optionalDestination);
}
return retVal;
}
std::shared_ptr<InternalControlFunction> TimeDateInterface::get_control_function() const
{
return myControlFunction;
}
void TimeDateInterface::process_rx_message(const CANMessage &message, void *parentPointer)
{
auto timeDateInterface = static_cast<TimeDateInterface *>(parentPointer);
if ((nullptr != timeDateInterface) &&
(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate) == message.get_identifier().get_parameter_group_number()) &&
(nullptr != message.get_source_control_function()))
{
if (CAN_DATA_LENGTH == message.get_data_length())
{
TimeAndDateInformation timeAndDateInformation;
timeAndDateInformation.controlFunction = message.get_source_control_function();
timeAndDateInformation.timeAndDate.seconds = message.get_uint8_at(0) / 4; // This is SPN 959
timeAndDateInformation.timeAndDate.milliseconds = static_cast<std::uint16_t>((message.get_uint8_at(0) % 4) * 250); // This is also part of SPN 959
timeAndDateInformation.timeAndDate.minutes = message.get_uint8_at(1); // This is SPN 960
timeAndDateInformation.timeAndDate.hours = message.get_uint8_at(2); // This is SPN 961
timeAndDateInformation.timeAndDate.month = message.get_uint8_at(3); // This is SPN 963
timeAndDateInformation.timeAndDate.day = message.get_uint8_at(4) / 4; // This is SPN 962
timeAndDateInformation.timeAndDate.quarterDays = message.get_uint8_at(4) % 4; // This is also part of SPN 962
timeAndDateInformation.timeAndDate.year = static_cast<std::uint16_t>(message.get_uint8_at(5) + 1985); // This is SPN 964
timeAndDateInformation.timeAndDate.localMinuteOffset = static_cast<std::int8_t>(message.get_uint8_at(6) - 125); // This is SPN 1601
timeAndDateInformation.timeAndDate.localHourOffset = static_cast<std::int8_t>(message.get_int8_at(7) - 125); // This is SPN 1602
#ifndef DISABLE_CAN_STACK_LOGGER
if (CANStackLogger::get_log_level() == CANStackLogger::LoggingLevel::Debug) // This is a very heavy log statement, so only do it if we are logging at debug level
{
std::ostringstream oss;
oss << "[Time/Date]: Control Function 0x";
oss << std::setfill('0') << std::setw(16) << std::hex << message.get_source_control_function()->get_NAME().get_full_name();
oss << " at address " << static_cast<int>(message.get_source_control_function()->get_address());
oss << " reports it is: " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.hours)) << ":";
oss << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.minutes)) << ":";
oss << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.seconds));
oss << " on day " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.day));
oss << " of month " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.month));
oss << " in the year " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.year));
oss << " with a local offset of " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.localHourOffset));
oss << " hours and " << isobus::to_string(static_cast<int>(timeAndDateInformation.timeAndDate.localMinuteOffset)) << " minutes.";
CANStackLogger::debug(oss.str());
}
#endif
timeDateInterface->timeAndDateEventDispatcher.invoke(std::move(timeAndDateInformation));
}
else
{
CANStackLogger::warn("[Time/Date]: Received a Time/Date message with an invalid data length. DLC must be 8.");
}
}
}
bool TimeDateInterface::process_request_for_time_date(std::uint32_t parameterGroupNumber,
std::shared_ptr<ControlFunction>,
bool &acknowledge,
AcknowledgementType &,
void *parentPointer)
{
bool retVal = false;
if ((nullptr != parentPointer) &&
(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TimeDate) == parameterGroupNumber))
{
auto interface = static_cast<TimeDateInterface *>(parentPointer);
if ((nullptr != interface->myControlFunction) &&
(nullptr != interface->userTimeDateCallback))
{
TimeAndDate timeAndDateInformation;
if (interface->userTimeDateCallback(timeAndDateInformation)) // Getting the time and date information from the user callback
{
CANStackLogger::debug("[Time/Date]: Received a request for Time/Date information and interface is configured to reply. Sending Time/Date.");
retVal = interface->send_time_and_date(timeAndDateInformation);
acknowledge = false;
}
else
{
CANStackLogger::error("[Time/Date]: Your application failed to provide Time/Date information when requested! You are probably doing something wrong. The request may be NACKed as a result.");
}
}
}
return retVal;
}
} // namespace isobus