Goby v2
frontseat.cpp
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Goby Underwater Autonomy Project Libraries
8 // ("The Goby Libraries").
9 //
10 // The Goby Libraries are free software: you can redistribute them and/or modify
11 // them under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // The Goby Libraries are distributed in the hope that they will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
22 
23 #include "goby/common/logger.h"
24 #include "goby/util/sci.h"
25 
26 #include "goby/util/seawater/depth.h"
27 #include "goby/util/seawater/salinity.h"
28 #include "goby/util/seawater/swstate.h"
29 
30 #include "frontseat.h"
31 
32 namespace gpb = goby::moos::protobuf;
33 using goby::glog;
35 using namespace goby::common::logger;
36 using namespace goby::common::tcolor;
37 
38 FrontSeatInterfaceBase::FrontSeatInterfaceBase(const iFrontSeatConfig& cfg)
39  : cfg_(cfg), helm_state_(gpb::HELM_NOT_RUNNING), state_(gpb::INTERFACE_STANDBY),
40  start_time_(goby::common::goby_time<double>()),
41  last_frontseat_error_(gpb::ERROR_FRONTSEAT_NONE), last_helm_error_(gpb::ERROR_HELM_NONE)
42 {
43  if (glog.is(DEBUG1))
44  {
45  signal_raw_from_frontseat.connect(
46  boost::bind(&FrontSeatInterfaceBase::glog_raw, this, _1, DIRECTION_FROM_FRONTSEAT));
47  signal_raw_to_frontseat.connect(
48  boost::bind(&FrontSeatInterfaceBase::glog_raw, this, _1, DIRECTION_TO_FRONTSEAT));
49  }
50 
51  if (!geodesy_.Initialise(cfg_.common().lat_origin(), cfg_.common().lon_origin()))
52  {
53  glog.is(DIE) && glog << "Failed to initialize MOOS Geodesy. Check LatOrigin and LongOrigin."
54  << std::endl;
55  }
56 
57  glog_out_group_ = "FrontSeatInterfaceBase::raw::out";
58  glog_in_group_ = "FrontSeatInterfaceBase::raw::in";
59 
60  goby::glog.add_group(glog_out_group_, goby::common::Colors::lt_magenta);
61  goby::glog.add_group(glog_in_group_, goby::common::Colors::lt_blue);
62 }
63 
64 void FrontSeatInterfaceBase::do_work()
65 {
66  try
67  {
68  check_change_state();
69  loop();
70  }
71  catch (FrontSeatException& e)
72  {
73  if (e.is_helm_error())
74  {
75  last_helm_error_ = e.helm_err();
76  state_ = gpb::INTERFACE_HELM_ERROR;
77  signal_state_change(state_);
78  }
79  else if (e.is_fs_error())
80  {
81  last_frontseat_error_ = e.fs_err();
82  state_ = gpb::INTERFACE_FS_ERROR;
83  signal_state_change(state_);
84  }
85  else
86  throw;
87  }
88 }
89 void FrontSeatInterfaceBase::check_change_state()
90 {
91  // check and change state
92  gpb::InterfaceState previous_state = state_;
93  switch (state_)
94  {
95  case gpb::INTERFACE_STANDBY:
96  if (frontseat_providing_data())
97  state_ = gpb::INTERFACE_LISTEN;
98  else
99  check_error_states();
100  break;
101 
102  case gpb::INTERFACE_LISTEN:
103  if (frontseat_state() == gpb::FRONTSEAT_ACCEPTING_COMMANDS &&
104  (helm_state() == gpb::HELM_DRIVE || !cfg_.require_helm()))
105  state_ = gpb::INTERFACE_COMMAND;
106  else
107  check_error_states();
108  break;
109 
110  case gpb::INTERFACE_COMMAND:
111  if (frontseat_state() == gpb::FRONTSEAT_IN_CONTROL ||
112  frontseat_state() == gpb::FRONTSEAT_IDLE)
113  state_ = gpb::INTERFACE_LISTEN;
114  else
115  check_error_states();
116  break;
117 
118  case gpb::INTERFACE_HELM_ERROR:
119  // clear helm error states if appropriate
120  if (helm_state() == gpb::HELM_DRIVE)
121  {
122  last_helm_error_ = gpb::ERROR_HELM_NONE;
123  state_ = gpb::INTERFACE_STANDBY;
124  }
125  break;
126 
127  case gpb::INTERFACE_FS_ERROR:
128  // clear frontseat error states if appropriate
129  if (last_frontseat_error_ == gpb::ERROR_FRONTSEAT_NOT_CONNECTED &&
130  frontseat_state() != gpb::FRONTSEAT_NOT_CONNECTED)
131  {
132  last_frontseat_error_ = gpb::ERROR_FRONTSEAT_NONE;
133  state_ = gpb::INTERFACE_STANDBY;
134  }
135  else if (last_frontseat_error_ == gpb::ERROR_FRONTSEAT_NOT_PROVIDING_DATA &&
136  frontseat_providing_data())
137  {
138  last_frontseat_error_ = gpb::ERROR_FRONTSEAT_NONE;
139  state_ = gpb::INTERFACE_STANDBY;
140  }
141 
142  break;
143  }
144  if (state_ != previous_state)
145  signal_state_change(state_);
146 }
147 
148 void FrontSeatInterfaceBase::check_error_states()
149 {
150  // helm in park is always an error
151  if (helm_state() == gpb::HELM_PARK)
152  throw(FrontSeatException(gpb::ERROR_HELM_PARKED));
153  // while in command, if the helm is not running, this is an error after
154  // a configurable timeout, unless require_helm is false
155  else if (cfg_.require_helm() &&
156  (helm_state() == gpb::HELM_NOT_RUNNING &&
157  (state_ == gpb::INTERFACE_COMMAND ||
158  (start_time_ + cfg_.helm_running_timeout() < goby_time<double>()))))
159  throw(FrontSeatException(gpb::ERROR_HELM_NOT_RUNNING));
160 
161  // frontseat not connected is an error except in standby, it's only
162  // an error after a timeout
163  if (frontseat_state() == gpb::FRONTSEAT_NOT_CONNECTED &&
164  (state_ != gpb::INTERFACE_STANDBY ||
165  start_time_ + cfg_.frontseat_connected_timeout() < goby_time<double>()))
166  throw(FrontSeatException(gpb::ERROR_FRONTSEAT_NOT_CONNECTED));
167  // frontseat must always provide data in either the listen or command states
168  else if (!frontseat_providing_data() && state_ != gpb::INTERFACE_STANDBY)
169  throw(FrontSeatException(gpb::ERROR_FRONTSEAT_NOT_PROVIDING_DATA));
170 }
171 
172 void FrontSeatInterfaceBase::glog_raw(const gpb::FrontSeatRaw& raw_msg, Direction direction)
173 {
174  if (direction == DIRECTION_TO_FRONTSEAT)
175  glog << group(glog_out_group_);
176  else if (direction == DIRECTION_FROM_FRONTSEAT)
177  glog << group(glog_in_group_);
178 
179  switch (raw_msg.type())
180  {
181  case gpb::FrontSeatRaw::RAW_ASCII:
182  glog << raw_msg.raw() << "\n"
183  << "^ " << magenta << raw_msg.description() << nocolor << std::endl;
184  break;
185  case gpb::FrontSeatRaw::RAW_BINARY:
186  glog << raw_msg.raw().size() << "byte message\n"
187  << "^ " << magenta << raw_msg.description() << nocolor << std::endl;
188  break;
189  }
190 };
191 
192 void FrontSeatInterfaceBase::compute_missing(gpb::CTDSample* ctd_sample)
193 {
194  double pressure_dbar = ctd_sample->pressure() / 10000;
195  if (!ctd_sample->has_salinity())
196  {
197  double conductivity_mSiemens_cm = ctd_sample->conductivity() * 10;
198  double temperature_deg_C = ctd_sample->temperature();
199  ctd_sample->set_salinity(SalinityCalculator::salinity(conductivity_mSiemens_cm,
200  temperature_deg_C, pressure_dbar));
201  ctd_sample->set_salinity_algorithm(gpb::CTDSample::UNESCO_44_PREKIN_AND_LEWIS_1980);
202  }
203  if (!ctd_sample->has_depth())
204  {
205  ctd_sample->set_depth(pressure2depth(pressure_dbar, ctd_sample->lat()));
206  }
207  if (!ctd_sample->has_sound_speed())
208  {
209  ctd_sample->set_sound_speed(goby::util::mackenzie_soundspeed(
210  ctd_sample->temperature(), ctd_sample->salinity(), ctd_sample->depth()));
211 
212  ctd_sample->set_sound_speed_algorithm(gpb::CTDSample::MACKENZIE_1981);
213  }
214  if (!ctd_sample->has_density())
215  {
216  ctd_sample->set_density(
217  density_anomaly(ctd_sample->salinity(), ctd_sample->temperature(), pressure_dbar) +
218  1000.0);
219 
220  ctd_sample->set_density_algorithm(gpb::CTDSample::UNESCO_38_MILLERO_AND_POISSON_1981);
221  }
222 }
223 
224 void FrontSeatInterfaceBase::compute_missing(gpb::NodeStatus* status)
225 {
226  if (!status->has_name())
227  status->set_name(cfg_.common().community());
228  if (!status->has_modem_id())
229  status->set_modem_id(cfg_.modem_id());
230 
231  if (!status->has_global_fix() && !status->has_local_fix())
232  {
233  glog.is(WARN) &&
234  glog << "Cannot 'compute_missing' on NodeStatus when global_fix and local_fix are both "
235  "missing (cannot make up a position from nothing)!"
236  << std::endl;
237  return;
238  }
239  else if (!status->has_global_fix())
240  {
241  // compute global from local
242  if (status->local_fix().has_z())
243  status->mutable_global_fix()->set_depth(-status->local_fix().z());
244 
245  double lat, lon;
246  geodesy_.UTM2LatLong(status->local_fix().x(), status->local_fix().y(), lat, lon);
247  status->mutable_global_fix()->set_lat(lat);
248  status->mutable_global_fix()->set_lon(lon);
249  }
250  else if (!status->has_local_fix())
251  {
252  // compute local from global
253  if (status->global_fix().has_depth())
254  status->mutable_local_fix()->set_z(-status->global_fix().depth());
255 
256  double x, y;
257  geodesy_.LatLong2LocalUTM(status->global_fix().lat(), status->global_fix().lon(), y, x);
258  status->mutable_local_fix()->set_x(x);
259  status->mutable_local_fix()->set_y(y);
260  }
261 }
Contains functions for adding color to Terminal window streams.
Definition: term_color.h:54
std::ostream & magenta(std::ostream &os)
All text following this manipulator is magenta (e.g. std::cout << magenta << "text";) ...
Definition: term_color.h:86
double goby_time< double >()
Returns current UTC time as seconds and fractional seconds since 1970-01-01 00:00:00.
Definition: time.h:130
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
Definition: time.h:104
void add_group(const std::string &name, Colors::Color color=Colors::nocolor, const std::string &description="")
Add another group to the logger. A group provides related manipulator for categorizing log messages...
common::FlexOstream glog
Access the Goby logger through this object.
The global namespace for the Goby project.
std::ostream & nocolor(std::ostream &os)
All text following this manipulator is uncolored (e.g. std::cout << green << "green" << nocolor << "u...
Definition: term_color.h:104