11/* *
2- * @file DeviceFactory.cpp
2+ * @file DeviceFactory.cpp
33 * @brief ${BRIEF_DESC}
44 *
55 * @author Kyle Mallory on 3/10/25.
@@ -24,63 +24,120 @@ void DeviceFactory::locateDevices(std::function<bool(DeviceFactory*, const dev_i
2424}
2525
2626/* *
27- * Attempts to identify a specific type of device on the specified port, within the timeout period.
28- * @param deviceCallback a function to be called if this Factory identified a possible/viable Inertial Sense device on the specified port
29- * @param port the port to check for an Inertial Sense device. In most uses, the port specified should already be opened, however if the
30- * port is not opened, this function will attempt to open it in order for ensure discovery.
31- * @param hdwId a hardware Id qualifier that can be used to narrow the type of device. There is no direct indication that a hardware type
32- * failed to match.
33- * @param timeout the maximum time to attempt to identify a device before giving up. There is no direct indication that a timeout occurred.
34- * @return true if a device was detected, otherwise false. Note that a false can result for any number of reasons, including invalid port,
35- * hdwId mismatch, or a timeout.
27+ * Blocking single-port discovery. Implemented as a wrapper around the three-phase validation protocol.
3628 */
3729bool DeviceFactory::locateDevice (std::function<bool (DeviceFactory*, const dev_info_t &, port_handle_t )>& deviceCallback, port_handle_t port, uint16_t hdwId, uint16_t timeoutMs) {
38- if (!portIsValid (port))
39- return false ; // TODO: Should we do anything special if the port is invalid? Really, we should never get here with an invalid port...
30+ // Check for existing device on this port
31+ device_handle_t dev = DeviceManager::getInstance ().getDevice (port);
32+ if (dev) {
33+ if (dev->matchesHdwId (hdwId)) {
34+ if (!dev->port )
35+ dev->assignPort (port);
36+ return dev->validate (timeoutMs);
37+ }
38+ return false ;
39+ }
40+
41+ // Phase 1: begin validation
42+ auto ctx = beginValidation (port, hdwId, timeoutMs);
43+ if (!ctx)
44+ return false ;
45+
46+ // Phase 2: blocking loop
47+ while (!ctx->complete ) {
48+ stepValidation (*ctx);
49+ if (!ctx->complete )
50+ SLEEP_MS (2 );
51+ }
4052
41- // can we open the port?
42- if (!portIsOpened (port)) {
43- log_debug (IS_LOG_DEVICE_FACTORY, " Opening serial port '%s'" , portName (port));
53+ // Phase 3: complete validation
54+ dev_info_t devInfo;
55+ if (completeValidation (*ctx, devInfo)) {
56+ return deviceCallback (this , devInfo, port);
57+ }
58+ return false ;
59+ }
60+
61+ /* *
62+ * Phase 1: Opens the port, validates it, creates a base ISDevice for probing, and calls the
63+ * onBeginValidation() hook to allow the factory to decline or do custom setup.
64+ */
65+ std::unique_ptr<DeviceFactory::ValidationContext> DeviceFactory::beginValidation (port_handle_t port, uint16_t hdwId, uint32_t timeoutMs, std::shared_ptr<ISDevice> sharedDevice) {
66+ if (!portIsValid (port)) {
67+ log_more_debug (IS_LOG_DEVICE_FACTORY, " beginValidation: port '%s' is invalid, skipping." , portName (port));
68+ return nullptr ;
69+ }
70+
71+ // Open port if needed (only when no shared device provided, i.e. standalone/blocking path)
72+ if (!sharedDevice && !portIsOpened (port)) {
4473 if (!portValidate (port) || (portOpen (port) != PORT_ERROR__NONE)) {
45- log_debug (IS_LOG_DEVICE_FACTORY, " Error opening serial port '%s'. Ignoring. Error was: %s" , portName (port), SERIAL_PORT (port)->error );
46- portClose (port); // failed to open
74+ portClose (port);
4775 portInvalidate (port);
48- return false ;
49- // m_ignoredPorts.push_back(curPortName); // record this port name as bad, so we don't try and reopen it again
76+ return nullptr ;
5077 }
5178 }
5279
53- // We can only validate devices connected on COMM ports, since ISComm is needed to parse/communicate with the device
54- if (!(portType (port) & PORT_TYPE__COMM))
55- return false ;
80+ // Only COMM ports can be validated via ISComm protocol
81+ if (!(portType (port) & PORT_TYPE__COMM)) {
82+ log_more_debug (IS_LOG_DEVICE_FACTORY, " beginValidation: port '%s' is not a COMM port (type=0x%04X), skipping." , portName (port), portType (port));
83+ return nullptr ;
84+ }
5685
5786 if (timeoutMs <= 0 )
5887 timeoutMs = deviceTimeout;
5988
60- // at this point, the port should be opened...
61- device_handle_t dev = DeviceManager::getInstance ().getDevice (port);
62- if (!dev) {
63- // no previous device exists, so identify the device and then register it with the manager
64- int validationResult = 0 ;
65- ISDevice localDev (hdwId, port);
66- do {
67- is_comm_port_parse_messages (port); // Read data directly into comm buffer and call callback functions
68- validationResult = localDev.validateAsync (timeoutMs);
69- SLEEP_MS (2 );
70- } while (!validationResult);
89+ // Let the factory decline this port
90+ if (!onBeginValidation (port, hdwId)) {
91+ log_more_debug (IS_LOG_DEVICE_FACTORY, " beginValidation: factory declined port '%s'." , portName (port));
92+ return nullptr ;
93+ }
7194
72- if (localDev.hasDeviceInfo () && localDev.matchesHdwId (hdwId)) {
73- // note that the deviceHandler callback can still reject the device for reasons
74- return deviceCallback (this , localDev.devInfo , port);
75- }
76- } else if (dev->matchesHdwId (hdwId)) {
77- if (!dev->port )
78- dev->assignPort (port);
79- else if (dev->port != port) {
80- // FIXME: this isn't good - it shouldn't happen, but it might... we should deal with it.
81- }
82- // a device exists associated with this port already, there isn't anything to do.
83- return dev->validate (timeoutMs);
95+ log_more_debug (IS_LOG_DEVICE_FACTORY, " beginValidation: created validation context for port '%s' (hdwId=0x%04X, timeout=%dms)" , portName (port), hdwId, timeoutMs);
96+ auto ctx = std::make_unique<ValidationContext>();
97+ ctx->port = port;
98+ ctx->device = sharedDevice ? sharedDevice : std::make_shared<ISDevice>(hdwId, port);
99+ ctx->hdwId = hdwId;
100+ ctx->timeoutMs = timeoutMs;
101+ return ctx;
102+ }
103+
104+ /* *
105+ * Phase 2: Reads pending data from the port, then calls the onStepValidation() hook to
106+ * advance validation state.
107+ */
108+ int DeviceFactory::stepValidation (ValidationContext& ctx) {
109+ if (ctx.complete )
110+ return ctx.result ;
111+
112+ is_comm_port_parse_messages (ctx.port );
113+ ctx.result = onStepValidation (*ctx.device , ctx.timeoutMs );
114+ ctx.complete = (ctx.result != 0 );
115+ if (ctx.result == 1 ) {
116+ log_debug (IS_LOG_DEVICE_FACTORY, " stepValidation: port '%s' validated successfully." , portName (ctx.port ));
117+ } else if (ctx.result == -1 ) {
118+ log_debug (IS_LOG_DEVICE_FACTORY, " stepValidation: port '%s' timed out." , portName (ctx.port ));
84119 }
85- return false ;
120+ return ctx. result ;
86121}
122+
123+ /* *
124+ * Phase 3: Checks that validation succeeded and the device has info, then calls the
125+ * onCompleteValidation() hook to let the factory accept or reject.
126+ */
127+ bool DeviceFactory::completeValidation (ValidationContext& ctx, dev_info_t & devInfoOut) {
128+ if (ctx.result <= 0 )
129+ return false ;
130+ if (!ctx.device || !ctx.device ->hasDeviceInfo ()) {
131+ log_debug (IS_LOG_DEVICE_FACTORY, " completeValidation: port '%s' validated but has no device info." , portName (ctx.port ));
132+ return false ;
133+ }
134+ if (!onCompleteValidation (ctx.device ->devInfo , ctx.hdwId )) {
135+ log_debug (IS_LOG_DEVICE_FACTORY, " completeValidation: factory rejected device on port '%s' (hdwId=0x%04X)." , portName (ctx.port ), ctx.hdwId );
136+ return false ;
137+ }
138+
139+ devInfoOut = ctx.device ->devInfo ;
140+ log_debug (IS_LOG_DEVICE_FACTORY, " completeValidation: factory accepted device %s on port '%s'." ,
141+ ISDevice::getIdAsString (devInfoOut).c_str (), portName (ctx.port ));
142+ return true ;
143+ }
0 commit comments