5cb1f2440784eec4419e40823031ec64e3ece05b
[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                 bitfield_encode_float((float)value,
46                                                         signal->get_bit_position(),
47                                                         signal->get_bit_size(),
48                                                         signal->get_factor(),
49                                                         signal->get_offset(),
50                                                         cf.data,
51                                                         CAN_MAX_DLEN);
52         }
53
54         return cf;
55 }
56
57 /// @brief Encode a boolean into an integer, fit for a CAN signal bitfield.
58 ///
59 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
60 /// bool*) that takes care of creating the DynamicField object for you with the
61 /// boolean value.
62 ///
63 /// @param[in] signal  - The CAN signal to encode this value for..
64 /// @param[in] value - The boolean value to encode
65 /// @param[out] send - An output argument that will be set to false if the value should
66 ///     not be sent for any reason.
67 ///
68 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
69 /// not be encoded and the return value is undefined.
70 ///
71 uint64_t encoder_t::encode_boolean(const can_signal_t& signal, bool value, bool* send)
72 {
73         return encode_number(signal, float(value), send);
74 }
75 /// @brief Encode a float into an integer, fit for a CAN signal's bitfield.
76 ///
77 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
78 /// bool*) that takes care of creating the DynamicField object for you with the
79 /// float value.
80 ///
81 /// @param[in] signal  - The CAN signal to encode this value for.
82 /// @param[in] value - The float value to encode.
83 /// @param[out] send - This output argument will always be set to false, so the caller will
84 ///      know not to publish this value to the pipeline.
85 ///
86 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
87 /// not be encoded and the return value is undefined.
88 ///
89 uint64_t encoder_t::encode_number(const can_signal_t& signal, float value, bool* send)
90 {
91         return float_to_fixed_point(value, signal.get_factor(), signal.get_offset());
92 }
93
94 /// @brief Encode a string into an integer, fit for a CAN signal's bitfield.
95 ///
96 /// Be aware that the behavior is undefined if there are multiple values assigned
97 /// to a single state. See https://github.com/openxc/vi-firmware/issues/185.
98 ///
99 /// This is a shortcut for encodeDynamicField(CanSignal*, openxc_DynamicField*,
100 /// bool*) that takes care of creating the DynamicField object for you with the
101 /// string state value.
102 ///
103 /// @param[in] signal  - The details of the signal that contains the state mapping.
104 /// @param[in] value - The string state value to encode.
105 /// @param[out] send - An output argument that will be set to false if the value should
106 ///     not be sent for any reason.
107 ///
108 /// @return Returns the encoded integer. If 'send' is changed to false, the field could
109 /// not be encoded and the return value is undefined.
110 ///
111 uint64_t encoder_t::encode_state(const can_signal_t& signal, const std::string& state, bool* send)
112 {
113         uint64_t value = 0;
114         if(state == "")
115         {
116                 AFB_DEBUG("Can't write state of "" -- not sending");
117                 *send = false;
118         }
119         else
120         {
121                 uint64_t signal_state = signal.get_states(state);
122                 if(signal_state != -1) {
123                         value = signal_state;
124                 } else {
125                         AFB_DEBUG("Couldn't find a valid signal state for %s", state.c_str());
126                         *send = false;
127                 }
128         }
129         return value;
130 }
131
132 /// @brief Parse a signal from a CAN message and apply any required
133 /// transforations to get a human readable value.
134 ///
135 /// If the can_signal_t has a non-NULL 'decoder' field, the raw CAN signal value
136 /// will be passed to the decoder before returning.
137 ///
138 /// @param[in] signal - The details of the signal to decode and forward.
139 /// @param[in] value - The numerical value that will be converted to a boolean.
140 /// @param[out] send - An output parameter that will be flipped to false if the value could
141 ///      not be decoded.
142 ///
143 /// @return The decoder returns an openxc_DynamicField, which may contain a number,
144 /// string or boolean. If 'send' is false, the return value is undefined.
145 ///
146 uint64_t encoder_t::encode_DynamicField( can_signal_t& signal, const openxc_DynamicField& field, bool* send)
147 {
148         uint64_t value = 0;
149         switch(field.type) {
150                 case openxc_DynamicField_Type_STRING:
151                         value = encode_state(signal, field.string_value, send);
152                         break;
153                 case openxc_DynamicField_Type_NUM:
154                         value = encode_number(signal, (float)field.numeric_value, send);
155                         break;
156                 case openxc_DynamicField_Type_BOOL:
157                         value = encode_boolean(signal, field.boolean_value, send);
158                         break;
159                 default:
160                         AFB_DEBUG("Dynamic field didn't have a value, can't encode");
161                         *send = false;
162                         break;
163         }
164         return value;
165 }