Correctly reconstituting entire CAN frame
[apps/low-level-can-service.git] / low-can-binding / can / can-encoder.cpp
1 /*
2  * Copyright (C) 2015, 2016 "IoT.bzh"
3  * Author "Romain Forlot" <romain.forlot@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include "can-encoder.hpp"
19
20 #include "canutil/write.h"
21 #include "../utils/openxc-utils.hpp"
22 #include "can-message-definition.hpp"
23 #include "../binding/low-can-hat.hpp"
24
25 /// @brief Write a value into a CAN signal in the destination buffer.
26 ///
27 /// @param[in] signal - The CAN signal to write, including the bit position and bit size.
28 /// @param[in] value - The encoded integer value to write into the CAN signal.
29 /// @param[out] data - The destination buffer.
30 /// @param[in] length - The length of the destination buffer.
31 ///
32 /// @return Returns a can_frame struct initialized and ready to be send.
33 const can_frame encoder_t::build_frame(const std::shared_ptr<can_signal_t>& signal, uint64_t value)
34 {
35         struct can_frame cf;
36         ::memset(&cf, 0, sizeof(cf));
37
38         cf.can_id = signal->get_message()->get_id();
39         cf.can_dlc = CAN_MAX_DLEN;
40
41         signal->set_last_value((float)value);
42
43         for(const auto& sig: signal->get_message()->get_can_signals())
44         {
45                 float last_value = sig->get_last_value();
46                 bitfield_encode_float(last_value,
47                                                         sig->get_bit_position(),
48                                                         sig->get_bit_size(),
49                                                         sig->get_factor(),
50                                                         sig->get_offset(),
51                                                         cf.data,
52                                                         CAN_MAX_DLEN);
53         }
54
55         return cf;
56 }
57
58 /// @brief Encode a boolean into an integer, fit for a CAN signal bitfield.
59 ///
60 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
61 /// bool*) that takes care of creating the DynamicField object for you with the
62 /// boolean value.
63 ///
64 /// @param[in] signal  - The CAN signal to encode this value for..
65 /// @param[in] value - The boolean value to encode
66 /// @param[out] send - An output argument that will be set to false if the value should
67 ///     not be sent for any reason.
68 ///
69 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
70 /// not be encoded and the return value is undefined.
71 ///
72 uint64_t encoder_t::encode_boolean(const can_signal_t& signal, bool value, bool* send)
73 {
74         return encode_number(signal, float(value), send);
75 }
76 /// @brief Encode a float into an integer, fit for a CAN signal's bitfield.
77 ///
78 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
79 /// bool*) that takes care of creating the DynamicField object for you with the
80 /// float value.
81 ///
82 /// @param[in] signal  - The CAN signal to encode this value for.
83 /// @param[in] value - The float value to encode.
84 /// @param[out] send - This output argument will always be set to false, so the caller will
85 ///      know not to publish this value to the pipeline.
86 ///
87 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
88 /// not be encoded and the return value is undefined.
89 ///
90 uint64_t encoder_t::encode_number(const can_signal_t& signal, float value, bool* send)
91 {
92         return float_to_fixed_point(value, signal.get_factor(), signal.get_offset());
93 }
94
95 /// @brief Encode a string into an integer, fit for a CAN signal's bitfield.
96 ///
97 /// Be aware that the behavior is undefined if there are multiple values assigned
98 /// to a single state. See https://github.com/openxc/vi-firmware/issues/185.
99 ///
100 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
101 /// bool*) that takes care of creating the DynamicField object for you with the
102 /// string state value.
103 ///
104 /// @param[in] signal  - The details of the signal that contains the state mapping.
105 /// @param[in] value - The string state value to encode.
106 /// @param[out] send - An output argument that will be set to false if the value should
107 ///     not be sent for any reason.
108 ///
109 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
110 /// not be encoded and the return value is undefined.
111 ///
112 uint64_t encoder_t::encode_state(const can_signal_t& signal, const std::string& state, bool* send)
113 {
114         uint64_t value = 0;
115         if(state == "")
116         {
117                 AFB_DEBUG("Can't write state of "" -- not sending");
118                 *send = false;
119         }
120         else
121         {
122                 uint64_t signal_state = signal.get_states(state);
123                 if(signal_state != -1) {
124                         value = signal_state;
125                 } else {
126                         AFB_DEBUG("Couldn't find a valid signal state for %s", state.c_str());
127                         *send = false;
128                 }
129         }
130         return value;
131 }
132
133 /// @brief Parse a signal from a CAN message and apply any required
134 /// transforations to get a human readable value.
135 ///
136 /// If the can_signal_t has a non-NULL 'decoder' field, the raw CAN signal value
137 /// will be passed to the decoder before returning.
138 ///
139 /// @param[in] signal - The details of the signal to decode and forward.
140 /// @param[in] value - The numerical value that will be converted to a boolean.
141 /// @param[out] send - An output parameter that will be flipped to false if the value could
142 ///      not be decoded.
143 ///
144 /// @return The decoder returns an openxc_DynamicField, which may contain a number,
145 /// string or boolean. If 'send' is false, the return value is undefined.
146 ///
147 uint64_t encoder_t::encode_DynamicField( can_signal_t& signal, const openxc_DynamicField& field, bool* send)
148 {
149         uint64_t value = 0;
150         switch(field.type) {
151                 case openxc_DynamicField_Type_STRING:
152                         value = encode_state(signal, field.string_value, send);
153                         break;
154                 case openxc_DynamicField_Type_NUM:
155                         value = encode_number(signal, (float)field.numeric_value, send);
156                         break;
157                 case openxc_DynamicField_Type_BOOL:
158                         value = encode_boolean(signal, field.boolean_value, send);
159                         break;
160                 default:
161                         AFB_DEBUG("Dynamic field didn't have a value, can't encode");
162                         *send = false;
163                         break;
164         }
165         return value;
166 }