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