Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft for Discussion: mlawlerau #36

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

mlawlerau
Copy link

Hi Mickey, my first GitHub PR so i might be missing or confused about somethings so help me out if I've got something wrong. Here is a draft PR of all my current changes. I've documented them below. Not expecting to complete this PR, just wanted you to see what I have been doing. Happy to discuss why i changed things and perhaps you might suggest better ways of doing things. I am new at objective-c and swift, coming from a java/groovy background.

LTODB2Adapter

  • Made enums Swift friendly

LTOBD2Command

  • Added 'LTResponseDataType' enum
  • Extended concepts of Command to support accessing the response value in a typed manner independently of a formatted string.
    • i.e. for a temperature, you can access the reponse as an integer or a double value that can be used in an application directly
    • this requires inserting a new conceptual layer 'decodeResponse' which can turn the unpacked array of bytes into a typed object, but not convert to string or apply formatting/units
    • formatted string variant of response value still available - formattedResponse() simply calls decodeResponse() to do the decoding first, before it applies formatting and units.
    • units and format strings generalised as configurable properties for abstract/concrete Command classes, enabling sensible defaults and overrides where necessary.
  • Introduced new method 'decodeResponse'
  • Renamed 'cookedResponse' to 'responsePayload'
  • Renamed 'didCookResponse' to 'didUnpackResponsePayload'

LTOBD2PID

  • Added 'LTOBD2PIDInteger' and 'LTOBD2PIDDouble' abstract subclassses for PIDs whose response payload decodes to single Integer and Double objects.
  • Enhanced concepts of a PID command to support
  • Updated various PID commands to extend new abstract subclassses.
  • More PID commands for query supported PID commands in additional hex ranges
  • Odometer command (untested, not supported by my car)

LTVIN

  • Added support for Model Year Range Discrimination (alas doesnt work for Porsche 997.2, plan to fix with pragmatic workaround)

LTBTLESerialTransporter

  • Added 'adapter' property to expose CBPeripheral
  • Temporary workaround to ignore Bluetooth BatteryMonitor
    • My car's Bluetooth Battery Monitor advertises with the same characteristcs as my ELM327 adapter.
    • Plan to extend functionality to support a known set of user-configurable identifiers to be excluded from connection

Localizable.strings

  • Minor typos
  • Add strings for Model Year decoding

 - Made enums Swift friendly

LTOBD2Command
 - Added 'LTResponseDataType' enum
 - Extended concepts of Command to support accessing the response value in a typed manner independently of a formatted string.
   - i.e. for a temperature, you can access the reponse as an integer or a double value that can be used in an application directly
   - this requires inserting a new conceptual layer 'decodeResponse' which can turn the unpacked array of bytes into a typed object, but not convert to string or apply formatting/units
   - formatted string variant of response value still available - formattedResponse() simply calls decodeResponse() to do the decoding first, before it applies formatting and unit.
   - units and format strings generalised as configurable properties for abstract/concrete Command classes, enabling sensible defaults and overrides where necessary.
 - Introduced new method 'decodeResponse'
 - Renamed 'cookedResponse' to 'responsePayload'
 - Renamed 'didCookResponse' to 'didUnpackResponsePayload'

LTOBD2PID
 - Added 'LTOBD2PIDInteger' and 'LTOBD2PIDDouble' abstract subclassses for PIDs whose response payload decodes to single Integer and Double objects.
 - Enhanced concepts of a PID command to support
 - Updated various PID commands to extend new abstract subclassses.
 - More PID commands for query supported PID commands in additional hex ranges
 - Odometer command (untested, not supported by my car)

LTVIN
 - Added support for Model Year Range Discrimination (alas doesnt work for Porsche 997.2, plan to fix with pragmatic workaround)

LTBTLESerialTransporter
 - Added 'adapter' property to expose CBPeripheral
 - Temporary workaround to ignore Bluetooth BatteryMonitor
   - My car's Bluetooth Battery Monitor advertises with the same characteristcs as my ELM327 adapter.
   - Plan to extend functionality to support a known set of user-configurable identifiers to be excluded from connection

Localizable.strings
 - Minor typos
 - Add strings for Model Year decoding
 - Made enums Swift friendly

LTOBD2Command
 - Added 'LTResponseDataType' enum
 - Extended concepts of Command to support accessing the response value in a typed manner independently of a formatted string.
   - i.e. for a temperature, you can access the reponse as an integer or a double value that can be used in an application directly
   - this requires inserting a new conceptual layer 'decodeResponse' which can turn the unpacked array of bytes into a typed object, but not convert to string or apply formatting/units
   - formatted string variant of response value still available - formattedResponse() simply calls decodeResponse() to do the decoding first, before it applies formatting and unit.
   - units and format strings generalised as configurable properties for abstract/concrete Command classes, enabling sensible defaults and overrides where necessary.
 - Introduced new method 'decodeResponse'
 - Renamed 'cookedResponse' to 'responsePayload'
 - Renamed 'didCookResponse' to 'didUnpackResponsePayload'

LTOBD2PID
 - Added 'LTOBD2PIDInteger' and 'LTOBD2PIDDouble' abstract subclassses for PIDs whose response payload decodes to single Integer and Double objects.
 - Enhanced concepts of a PID command to support
 - Updated various PID commands to extend new abstract subclassses.
 - More PID commands for query supported PID commands in additional hex ranges
 - Odometer command (untested, not supported by my car)

LTVIN
 - Added support for Model Year Range Discrimination (alas doesnt work for Porsche 997.2, plan to fix with pragmatic workaround)

LTBTLESerialTransporter
 - Added 'adapter' property to expose CBPeripheral
 - Temporary workaround to ignore Bluetooth BatteryMonitor
   - My car's Bluetooth Battery Monitor advertises with the same characteristcs as my ELM327 adapter.
   - Plan to extend functionality to support a known set of user-configurable identifiers to be excluded from connection

Localizable.strings
 - Minor typos
 - Add strings for Model Year decoding
@mickeyl
Copy link
Owner

mickeyl commented Mar 26, 2021

First off, sorry for the delay in answering. I'm super busy with a new project (more on that later, but it has to do with car diagnostics).

Thanks a lot for your pull request. I will start commenting on the individual parts of the pull request in detail right after finishing this message. In general, what we should aim for is more fine granular commits, so that we have a chance to retrace the history.

Please see the inline comments. Looking forward to merging this contribution!

Comment on lines +233 to +242

// Temp workaround to ignore pesky Bluetooth Battery Monitor
if ( [peripheral.name isEqualToString:@"Battery Monitor"] )
{
LOG( @"Peripheral '%@' is that darn Battery Monitor", peripheral.identifier );

[_manager cancelPeripheralConnection:peripheral];
[_possibleAdapters removeObject:peripheral];
return;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit unnecessary. I noticed you wrote "temp workaround", but still… usually you are querying for dedicated services – is that battery monitor of yours providing the same service UUID as one of your OBD2 adapters?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I have removed this from my code now. This won't be in my next PR.

@@ -61,7 +61,7 @@ -(void)didCompleteResponse:(NSArray<NSString*>*)lines protocol:(LTOBD2Protocol*)
{
if ( ! [lines.firstObject isEqualToString:RESPONSE_FINAL_NODATA] )
{
[_command didCookResponse:[protocol decode:lines originatingCommand:_command.commandString] withProtocolType:protocolType];
[_command didUnpackResponsePayload:[protocol decode:lines originatingCommand:_command.commandString] withProtocolType:protocolType];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this merely a cosmetic change or what is your reason renaming that?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, after reading more of it I get the semantic difference. Fine with me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@@ -3,6 +3,7 @@
//

#import "LTOBD2PID.h"
#import "float.h"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's just for the (double) case, we better #import <Foundation/Foundation.h> here.

In Objective-C-land we usually try to avoid the plain C headers like stdio.h, and friends.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will update.

@@ -90,6 +91,20 @@ -(instancetype)initWithString:(NSString*)string inFreezeFrameDTC:(LTOBD2DTC*)fre
#pragma mark -
#pragma mark API for subclasses

-(NSNumber*) decodeSingleByteDoubleValueWithOffset:(double)offset factor:(double)factor
Copy link
Owner

@mickeyl mickeyl Mar 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nitpick: Coding style on this library is to not have whitespace between the result type and the start of the method name. There's more of that below, will not add individual comments :-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, happy to align with coding style of the library. Consistency is important, I will fix where I see it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, think i got all of them

Comment on lines +153 to +227

/**
Units string used when building the formattedResponse
Default units is empty string.
Subclasses can override to define their own units.
*/
-(NSString*)units
{
return @"";
}

/**
Format string used when building the formattedResponse
Default format is simple string concatentation
Subclasses can override to define their own formatting for integer and double value types.
*/
-(NSString*)format
{
return @"%@";
}

/**
Declares the type of the decodedResponse object.
- String
- Integer
- Double
Default responseDataType is a String.
Subclasses can override to configure their own responseDataType.
*/
-(LTResponseDataType)responseDataType
{
return LTString;
}

/**
Default formattedResponse implementation
Uses the format string to format the decodeResponse object and then appends the units string.
Subclasses can override to customise their own formattedResponse string.
However, most commands do not need to customise this function, and can just configure format and units strings and provide an impl of decodeResponse.
*/
-(NSString*)formattedResponse
{
if ( !self.responsePayload )
{
return LTStringLookupWithPlaceholder(@"OBD2_NO_DATA", @"N/A");
}

NSObject* decodedResponse = self.decodeResponse;

LOG( @"decodedResponse: '%@'", decodedResponse );

NSString* responseString;

if (self.responseDataType == LTString) {
responseString = [NSString stringWithFormat:self.format, decodedResponse];
} else if (self.responseDataType == LTInteger) {
int decodedIntValue = ((NSNumber*) decodedResponse).intValue;
responseString = [NSString stringWithFormat:self.format, decodedIntValue ];
} else if (self.responseDataType == LTDouble) {
double decodedDoubleValue = ((NSNumber*) decodedResponse).doubleValue;
responseString = [NSString stringWithFormat:self.format, decodedDoubleValue ];
} else {
LOG( @"ERROR - UNHANDLED ResponseDataType: '%@'", self.responseDataType );
}

LOG( @"responseString after format: '%@': '%@'", self.format, responseString );

if (self.units) {
responseString = [NSString stringWithFormat: @"%@" UTF8_NARROW_NOBREAK_SPACE @"%@", responseString, self.units];
LOG( @"responseString with units: '%@'", responseString );
}

return responseString;
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of a unit concept is a great change in principle, something I would have wanted to implement right from the start, but never got around to actually doing it – instead we have to live with that inflexible formattedResponse that conveys little semantics.

That said though, if you are to introduce a unit concept, I'd rather see it utilizing the concepts from Foundation, which already contains classes such as NSUnit, NSMeasurement, and NSUnitFormatter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good idea. I will look at your suggestion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mickey, I have updated my changes to use NSUnit, NSMeasurement, and NSUnitFormatter. Will send a PR soon.

Comment on lines +55 to +67
static NSUInteger const MODEL_YEAR_RANGE_DISCRIMINATOR_LOCATION = 6;
static NSUInteger const MODEL_YEAR_CODE_LOCATION = 9;

if (isdigit([_vin characterAtIndex:MODEL_YEAR_RANGE_DISCRIMINATOR_LOCATION])) {
// if the character at index 6 is a numeric digit then we select our model year from the 'old' 1980-2009 range
NSString* modelYearCode = [@"VIN_MODEL_YEAR_OLD_" stringByAppendingString:[_vin substringWithRange:NSMakeRange(MODEL_YEAR_CODE_LOCATION, 1)]];
_modelYear = LTStringLookupWithPlaceholder( modelYearCode, UNASSIGNED );
} else {
// if the character at index 6 is not a numeric digit then we select our model year from the 'new' 2010-2029 range
NSString* modelYearCode = [@"VIN_MODEL_YEAR_NEW_" stringByAppendingString:[_vin substringWithRange:NSMakeRange(MODEL_YEAR_CODE_LOCATION, 1)]];
_modelYear = LTStringLookupWithPlaceholder( modelYearCode, UNASSIGNED );
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that really hold for all kind of VINs?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Wikipedia page: https://en.wikipedia.org/wiki/Vehicle_identification_number in section "Model Year Encoding" it says that:
On April 30, 2008, the US National Highway Traffic Safety Administration adopted a final rule amending 49 CFR Part 565, "so that the current 17 character vehicle identification number (VIN) system, which has been in place for almost 30 years, can continue in use for at least another 30 years", in the process making several changes to the VIN requirements applicable to all motor vehicles manufactured for sale in the United States:
In order to identify the exact year in passenger cars and multipurpose passenger vehicles with a GVWR of 10,000 or less, one must read position 7 as well as position 10. For passenger cars, and for multipurpose passenger vehicles and trucks with a gross vehicle weight rating of 10,000 lb (4,500 kg) or less, if position seven is numeric, the model year in position 10 of the VIN refers to a year in the range 1980–2009. If position seven is alphabetic, the model year in position 10 of VIN refers to a year in the range 2010–2039.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cant guarantee this and it probably needs more testing. But without something like this, then the library will return incorrect model year for most recent cars. At some point in time in a modern app we should err on expecting that a car is more likely to be 2010-2039 than be 1980-2009. Tough call. Perhaps you could add support here for a heuristic or algorithm that looked at other elements in the VIN or in the other PIDs to help decide if the car is 1980-2009 or 2010-2039.

Copy link
Owner

@mickeyl mickeyl Apr 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. The code is fine and will improve things. Given also that this VIN class is probably most often used to identify cars attached to an OBD2 adapter, the model year almost always has to be above 1996.

@mlawlerau
Copy link
Author

mlawlerau commented Mar 26, 2021 via email

@mlawlerau mlawlerau marked this pull request as draft April 19, 2021 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants