Skip to content

Commit 42f1701

Browse files
committed
plugins: add triggered conveyor belt (real)
- configuration of plugin set to simulation mode - fixes problem with adding multiple of copies of the same object to inventory - requires pull request rockin-robot-challenge/at_work_networked_device#4
1 parent 11f8b5a commit 42f1701

File tree

6 files changed

+420
-10
lines changed

6 files changed

+420
-10
lines changed

cfg/config.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ llsfrb:
1212
game: game.log
1313

1414
triggered-conveyor-belt:
15-
enable: false
15+
mode: simulation
1616
host: !ipv4 eth0
17-
command_port: !tcp-port 55502
18-
status_port: !tcp-port 55501
17+
camera_status_port: !tcp-port 55521
18+
conveyor_status_port: !tcp-port 55522
19+
command_port: !tcp-port 55523
1920

2021
drilling-machine:
2122
enable: false

rockin/plugins/triggered_conveyor_belt/Makefile

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,26 @@ REQ_BOOST_LIBS = thread system
2323
HAVE_BOOST_LIBS = $(call boost-have-libs,$(REQ_BOOST_LIBS))
2424

2525
LIBS_triggered_conveyor_belt = llsfrbcore llsfrbaspects rockin_plugin_msgs
26-
OBJS_triggered_conveyor_belt = triggered_conveyor_belt_plugin.o triggered_conveyor_belt_simulation_thread.o
26+
OBJS_triggered_conveyor_belt = triggered_conveyor_belt_plugin.o triggered_conveyor_belt_thread.o \
27+
triggered_conveyor_belt_simulation_thread.o
2728

28-
ifeq ($(HAVE_CLIPS)$(HAVE_BOOST_LIBS),11)
29+
ifeq ($(HAVE_CLIPS)$(HAVE_PROTOBUF)$(HAVE_LIBZMQ)$(HAVE_BOOST_LIBS),1111)
2930
OBJS_all = $(OBJS_triggered_conveyor_belt)
3031
PLUGINS_all = $(PLUGINDIR)/triggered_conveyor_belt.$(SOEXT)
3132

32-
CFLAGS += $(CFLAGS_CLIPS) $(call boost-libs-cflags,$(REQ_BOOST_LIBS))
33-
LDFLAGS += $(LDFLAGS_CLIPS) $(call boost-libs-ldflags,$(REQ_BOOST_LIBS))
33+
CFLAGS += $(CFLAGS_CLIPS) ${CFLAGS_PROTOBUF} ${CFLAGS_LIBZMQ} \
34+
$(call boost-libs-cflags,$(REQ_BOOST_LIBS))
35+
LDFLAGS += $(LDFLAGS_CLIPS) ${LDFLAGS_PROTOBUF} ${LDFLAGS_LIBZMQ} \
36+
$(call boost-libs-ldflags,$(REQ_BOOST_LIBS))
3437
else
3538
ifneq ($(HAVE_CLIPS),1)
3639
WARN_TARGETS += warning_clips
40+
endif
41+
ifneq ($(HAVE_PROTOBUF),1)
42+
WARN_TARGETS += warning_protobuf
43+
endif
44+
ifneq ($(HAVE_LIBZMQ),1)
45+
WARN_TARGETS += warning_libzmq
3746
endif
3847
ifneq ($(HAVE_BOOST_LIBS),1)
3948
WARN_TARGETS_BOOST = $(foreach l,$(REQ_BOOST_LIBS),$(if $(call boost-have-lib,$l),, warning_boost_$l))
@@ -43,8 +52,12 @@ endif
4352
ifeq ($(OBJSSUBMAKE),1)
4453
all: $(WARN_TARGETS) $(WARN_TARGETS_BOOST)
4554
.PHONY: warning_clips
55+
warning_libzmq:
56+
$(SILENT)echo -e "$(INDENT_PRINT)--> $(TRED)Cannot build Triggered Conveyor Belt Plugin$(TNORMAL) (libzmq not found)"
4657
warning_clips:
4758
$(SILENT)echo -e "$(INDENT_PRINT)--> $(TRED)Cannot build Triggered Conveyor Belt Plugin$(TNORMAL) (clipsmm not found)"
59+
warning_protobuf:
60+
$(SILENT)echo -e "$(INDENT_PRINT)--> $(TRED)Cannot build Triggered Conveyor Belt Plugin$(TNORMAL) (protobuf not found)"
4861
$(WARN_TARGETS_BOOST): warning_boost_%:
4962
$(SILENT)echo -e "$(INDENT_PRINT)--> $(TRED)Cannot build Triggered Conveyor Belt Plugin$(TNORMAL) (Boost library $* not found)"
5063
endif

rockin/plugins/triggered_conveyor_belt/triggered_conveyor_belt_plugin.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <core/plugin.h>
2222

23+
#include "triggered_conveyor_belt_thread.h"
2324
#include "triggered_conveyor_belt_simulation_thread.h"
2425

2526
using namespace fawkes;
@@ -40,9 +41,16 @@ class TriggeredConveyorBeltPlugin: public fawkes::Plugin
4041
TriggeredConveyorBeltPlugin(Configuration *config) :
4142
Plugin(config)
4243
{
43-
thread_list.push_back(new TriggeredConveyorBeltSimulationThread());
44+
if (config->exists("/llsfrb/triggered-conveyor-belt/mode"))
45+
{
46+
std::string config_mode = config->get_string("/llsfrb/triggered-conveyor-belt/mode");
47+
if (config_mode == std::string("real"))
48+
thread_list.push_back(new TriggeredConveyorBeltThread());
49+
else if (config_mode == std::string("simulation"))
50+
thread_list.push_back(new TriggeredConveyorBeltSimulationThread());
51+
}
4452
}
4553
};
4654

47-
PLUGIN_DESCRIPTION("Plugin to communicate with the triggered conveyor belt")
55+
PLUGIN_DESCRIPTION("Plugin to communicate with or simulate the triggered conveyor belt")
4856
EXPORT_PLUGIN(TriggeredConveyorBeltPlugin)
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
/***************************************************************************
2+
* triggered_conveyor_belt_thread.cpp - Thread to communicate with
3+
* the conveyor belt and
4+
* quality control camera.
5+
*
6+
* Created: Wed Oct 28 11:47:42 2015
7+
* Copyright 2015 Alexander Moriarty
8+
****************************************************************************/
9+
10+
/* This program is free software; you can redistribute it and/or modify
11+
* it under the terms of the GNU General Public License as published by
12+
* the Free Software Foundation; either version 2 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it 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 Library General Public License for more details.
19+
*
20+
* Read the full text in the LICENSE.GPL file in the doc directory.
21+
*/
22+
23+
#include "triggered_conveyor_belt_thread.h"
24+
25+
#include <core/threading/mutex_locker.h>
26+
#include <boost/lexical_cast.hpp>
27+
#include <boost/thread/thread.hpp>
28+
29+
/** @class TriggeredConveyorBeltThread
30+
* Thread to communicate with the @Work conveyor belt and quality control
31+
* camera.
32+
*
33+
* @author Alexander Moriarty
34+
*/
35+
36+
TriggeredConveyorBeltThread::TriggeredConveyorBeltThread() :
37+
Thread("TriggeredConveyorBeltThread", Thread::OPMODE_CONTINUOUS),
38+
zmq_context_(NULL), zmq_publisher_(NULL), zmq_camera_subscriber_(NULL),
39+
zmq_conveyor_subscriber_(NULL), cfg_timer_interval_(40),
40+
default_network_interface_("eth0"),
41+
last_state_(QualityControlCameraStatus::NO_PLATE)
42+
{
43+
}
44+
45+
void TriggeredConveyorBeltThread::init()
46+
{
47+
logger->log_info("TCB", " NEW TRIGGERED CONVEYOR BELT THREAD");
48+
try
49+
{
50+
cfg_timer_interval_ = config->get_uint("/llsfrb/clips/timer-interval");
51+
} catch (fawkes::Exception &e)
52+
{
53+
// use default value
54+
}
55+
56+
std::string host_ip_address = "";
57+
std::string host_command_port = "";
58+
std::string host_camera_status_port = "";
59+
std::string host_conveyor_status_port = "";
60+
61+
logger->log_info("TriggeredConveyorBelt", "TRYING TO CONNECT TO DEVICE");
62+
try {
63+
if (config->exists("/llsfrb/triggered-conveyor-belt/host")
64+
&& config->exists("/llsfrb/triggered-conveyor-belt/command_port")
65+
&& config->exists("/llsfrb/triggered-conveyor-belt/camera_status_port")
66+
&& config->exists("/llsfrb/triggered-conveyor-belt/conveyor_status_port"))
67+
{
68+
host_ip_address = config->get_string("/llsfrb/triggered-conveyor-belt/host");
69+
host_command_port = "epgm://" + default_network_interface_ + ":" + boost::lexical_cast<std::string>(config->get_uint("/llsfrb/triggered-conveyor-belt/command_port"));
70+
host_camera_status_port = "epgm://" + host_ip_address + ":" + boost::lexical_cast<std::string>(config->get_uint("/llsfrb/triggered-conveyor-belt/camera_status_port"));
71+
host_conveyor_status_port = "epgm://" + host_ip_address + ":" + boost::lexical_cast<std::string>(config->get_uint("/llsfrb/triggered-conveyor-belt/conveyor_status_port"));
72+
73+
zmq_context_ = new zmq::context_t(1);
74+
int msg_limit = 1;
75+
76+
// add publisher to send conveyor start command
77+
logger->log_info("TriggeredConveyorBelt", "Connecting to the command port: %s", host_command_port.c_str());
78+
zmq_publisher_ = new zmq::socket_t(*zmq_context_, ZMQ_PUB);
79+
zmq_publisher_->setsockopt(ZMQ_CONFLATE, &msg_limit, sizeof(msg_limit));
80+
zmq_publisher_->bind(host_command_port.c_str());
81+
82+
// add subscriber to receive status messages from camera
83+
logger->log_info("TriggeredConveyorBelt", "Connecting to camera status port: %s", host_camera_status_port.c_str());
84+
zmq_camera_subscriber_ = new zmq::socket_t(*zmq_context_, ZMQ_SUB);
85+
zmq_camera_subscriber_->setsockopt(ZMQ_SUBSCRIBE, "", 0);
86+
zmq_camera_subscriber_->setsockopt(ZMQ_CONFLATE, &msg_limit, sizeof(msg_limit));
87+
zmq_camera_subscriber_->connect(host_camera_status_port.c_str());
88+
logger->log_info("TriggeredConveyorBelt", "Connected to Camera Status");
89+
90+
// add subscriber to receive status messages from conveyor
91+
logger->log_info("TriggeredConveyorBelt", "Connecting to conveyor status port: %s", host_conveyor_status_port.c_str());
92+
zmq_conveyor_subscriber_ = new zmq::socket_t(*zmq_context_, ZMQ_SUB);
93+
zmq_conveyor_subscriber_->setsockopt(ZMQ_SUBSCRIBE, "", 0);
94+
zmq_conveyor_subscriber_->setsockopt(ZMQ_CONFLATE, &msg_limit, sizeof(msg_limit));
95+
zmq_conveyor_subscriber_->connect(host_conveyor_status_port.c_str());
96+
logger->log_info("TriggeredConveyorBelt", "Connected to Conveyor Status");
97+
98+
} else
99+
{
100+
logger->log_info("TriggeredConveyorBelt", "Invalid configuration for triggered conveyor belt.");
101+
}
102+
} catch (std::exception &e)
103+
{
104+
logger->log_warn("TriggeredConveyorBelt", "Cannot establish communication with the conveyor belt: %s", e.what());
105+
106+
delete zmq_context_;
107+
delete zmq_publisher_;
108+
delete zmq_camera_subscriber_;
109+
delete zmq_conveyor_subscriber_;
110+
111+
zmq_context_ = NULL;
112+
zmq_publisher_ = NULL;
113+
zmq_camera_subscriber_ = NULL;
114+
zmq_conveyor_subscriber_ = NULL;
115+
}
116+
117+
fawkes::MutexLocker lock(clips_mutex);
118+
119+
clips->add_function("conveyor-belt-start-belt", sigc::slot<void>(sigc::mem_fun(*this, &TriggeredConveyorBeltThread::clips_start_belt)));
120+
clips->add_function("conveyor-belt-stop-belt", sigc::slot<void>(sigc::mem_fun(*this, &TriggeredConveyorBeltThread::clips_stop_belt)));
121+
clips->add_function("conveyor-belt-is-running", sigc::slot<bool>(sigc::mem_fun(*this, &TriggeredConveyorBeltThread::clips_is_belt_running)));
122+
clips->add_function("conveyor-belt-is-device-connected", sigc::slot<bool>(sigc::mem_fun(*this, &TriggeredConveyorBeltThread::clips_is_belt_connected)));
123+
124+
if (!clips->build("(deffacts have-feature-conveyor-belt (have-feature ConveyorBelt))"))
125+
logger->log_warn("TriggeredConveyorBelt", "Failed to build deffacts have-feature-conveyor-belt");
126+
127+
128+
clips->add_function("quality-control-camera-is-device-connected", sigc::slot<bool>(sigc::mem_fun(*this, &TriggeredConveyorBeltThread::clips_is_camera_connected)));
129+
130+
if (!clips->build("(deffacts have-feature-quality-control-camera (have-feature QualityControlCamera))"))
131+
logger->log_warn("TriggeredConveyorBelt", "Failed to build deffacts have-feature-quality-control-camera");
132+
}
133+
134+
void TriggeredConveyorBeltThread::finalize()
135+
{
136+
delete zmq_context_;
137+
delete zmq_publisher_;
138+
delete zmq_camera_subscriber_;
139+
delete zmq_conveyor_subscriber_;
140+
141+
zmq_context_ = NULL;
142+
zmq_publisher_ = NULL;
143+
zmq_camera_subscriber_ = NULL;
144+
zmq_conveyor_subscriber_ = NULL;
145+
}
146+
147+
void TriggeredConveyorBeltThread::loop()
148+
{
149+
// unconditional sleep
150+
boost::this_thread::sleep(boost::posix_time::milliseconds(cfg_timer_interval_));
151+
receiveAndBufferConveyorStatusMsg();
152+
receiveAndBufferCameraStatusMsg();
153+
}
154+
155+
bool TriggeredConveyorBeltThread::clips_is_belt_running()
156+
{
157+
if (last_conveyor_status_msg_.has_mode()
158+
&& last_conveyor_status_msg_.mode() == START)
159+
{
160+
return true;
161+
} else
162+
return false;
163+
}
164+
165+
bool TriggeredConveyorBeltThread::clips_is_belt_connected()
166+
{
167+
if (last_conveyor_status_msg_.has_is_device_connected()
168+
&& last_conveyor_status_msg_.is_device_connected())
169+
{
170+
return true;
171+
} else
172+
return false;
173+
}
174+
175+
bool TriggeredConveyorBeltThread::clips_is_camera_connected()
176+
{
177+
if (last_camera_status_msg_.has_is_device_connected()
178+
&& last_camera_status_msg_.is_device_connected())
179+
{
180+
return true;
181+
} else
182+
return false;
183+
}
184+
185+
void TriggeredConveyorBeltThread::clips_start_belt()
186+
{
187+
setConveyorBeltRunMode(START);
188+
}
189+
190+
void TriggeredConveyorBeltThread::clips_stop_belt()
191+
{
192+
setConveyorBeltRunMode(STOP);
193+
}
194+
195+
void TriggeredConveyorBeltThread::setConveyorBeltRunMode(RunMode mode)
196+
{
197+
logger->log_info("TriggeredConveyorBelt", "Set run mode: %d", mode);
198+
199+
boost::posix_time::time_duration time_diff;
200+
201+
// prevent sending to many messages to the device
202+
time_diff = boost::posix_time::microsec_clock::local_time()
203+
- last_sent_command_timestamp_;
204+
if (time_diff.total_milliseconds() < 1000)
205+
return;
206+
207+
ConveyorBeltCommand command_msg;
208+
std::string serialized_string;
209+
210+
command_msg.set_mode(mode);
211+
212+
zmq::message_t *query = NULL;
213+
try
214+
{
215+
command_msg.SerializeToString(&serialized_string);
216+
query = new zmq::message_t(serialized_string.length());
217+
memcpy(query->data(), serialized_string.c_str(), serialized_string.length());
218+
zmq_publisher_->send(*query);
219+
220+
last_sent_command_timestamp_ = boost::posix_time::microsec_clock::local_time();
221+
222+
logger->log_info("TriggeredConveyorBelt", "Set run mode: %d", command_msg.mode());
223+
224+
delete query;
225+
} catch (fawkes::Exception &e)
226+
{
227+
logger->log_warn("TriggeredConveyorBelt", "Failed to set run mode: %s", e.what());
228+
229+
if (query != NULL)
230+
delete query;
231+
}
232+
233+
}
234+
235+
void TriggeredConveyorBeltThread::receiveAndBufferConveyorStatusMsg()
236+
{
237+
fawkes::MutexLocker lock(clips_mutex);
238+
239+
if (!zmq_conveyor_subscriber_){
240+
logger->log_warn("TriggeredConveyorBelt", "NO ZMQ CONVEYOR SUBSCRIBER");
241+
} else
242+
{
243+
if (zmq_conveyor_subscriber_->recv(&zmq_conveyor_msg_, ZMQ_NOBLOCK))
244+
{
245+
if (last_conveyor_status_msg_.ParseFromArray(zmq_conveyor_msg_.data(),
246+
zmq_conveyor_msg_.size()))
247+
{
248+
prev_conveyor_update_timestamp_ = boost::posix_time::microsec_clock::local_time();
249+
}
250+
} else
251+
{
252+
boost::posix_time::time_duration conveyor_time_diff = boost::posix_time::microsec_clock::local_time()
253+
- prev_conveyor_update_timestamp_;
254+
if (conveyor_time_diff.total_seconds() >= 3)
255+
{
256+
last_conveyor_status_msg_.set_is_device_connected(false);
257+
last_conveyor_status_msg_.set_mode(STOP);
258+
}
259+
}
260+
}
261+
}
262+
void TriggeredConveyorBeltThread::receiveAndBufferCameraStatusMsg()
263+
{
264+
fawkes::MutexLocker lock(clips_mutex);
265+
266+
if (!zmq_camera_subscriber_){
267+
logger->log_warn("TriggeredConveyorBelt", "NO ZMQ CAMERA SUBSCRIBER");
268+
} else
269+
{
270+
if (zmq_camera_subscriber_->recv(&zmq_camera_msg_, ZMQ_NOBLOCK))
271+
{
272+
if(last_camera_status_msg_.ParseFromArray(zmq_camera_msg_.data(),
273+
zmq_camera_msg_.size()))
274+
{
275+
// remember time of last received msg
276+
prev_camera_update_timestamp_ = boost::posix_time::microsec_clock::local_time();
277+
278+
std::string detected_plate;
279+
280+
switch (last_camera_status_msg_.state()) {
281+
case 0: detected_plate = "NO_PLATE"; break;
282+
case 1: detected_plate = "UNKNOWN_PLATE"; break;
283+
case 2: detected_plate = "FAULTY_PLATE"; break;
284+
case 3: detected_plate = "UNUSABLE_PLATE"; break;
285+
default: detected_plate = "UNKNOWN_PLATE"; break;
286+
}
287+
//logger->log_warn("TriggeredConveyorBelt", "DETECTED PLATE: %s", detected_plate.c_str());
288+
289+
if (last_state_ == last_camera_status_msg_.state())
290+
{
291+
return;
292+
} else
293+
{
294+
last_state_ = last_camera_status_msg_.state();
295+
// let CLIPS know about the plate by asserting it as a fact
296+
std::stringstream sstr;
297+
sstr << "(quality-control-camera-object " << detected_plate << ")";
298+
clips->assert_fact(sstr.str());
299+
}
300+
}
301+
} else
302+
{
303+
boost::posix_time::time_duration camera_time_diff = boost::posix_time::microsec_clock::local_time()
304+
- prev_camera_update_timestamp_;
305+
if (camera_time_diff.total_seconds() >= 3)
306+
{
307+
last_camera_status_msg_.set_is_device_connected(false);
308+
last_camera_status_msg_.set_state(QualityControlCameraStatus::UNKNOWN_PLATE);
309+
}
310+
}
311+
}
312+
}

0 commit comments

Comments
 (0)