Skip to content

Java: CPM v1.2.1 refactoring and new CPM v2.1.1#494

Open
mathieu1fb wants to merge 18 commits intoOrange-OpenSource:masterfrom
mathieu1fb:cpm-refactoring
Open

Java: CPM v1.2.1 refactoring and new CPM v2.1.1#494
mathieu1fb wants to merge 18 commits intoOrange-OpenSource:masterfrom
mathieu1fb:cpm-refactoring

Conversation

@mathieu1fb
Copy link
Collaborator

What's new

  • CPM v2.1.1 support has been added
  • CPM v1.2.1 has been refactored completely for compatibility, so that both versions can be handled in parallel

Close #492
Close #493


What to do

After reviewing code changes:

  1. Clone the its-client project on your IDE.
  2. Ensure that you have Gradle to be able to build the java/iot3 modules (core, mobility and examples).
  3. Find the Iot3MobilityExample class in the examples module, and set appropriate values for the following fields:
private static final String EXAMPLE_UUID = "uuid";
private static final String EXAMPLE_CONTEXT = "context";
// MQTT parameters
private static final String EXAMPLE_MQTT_HOST = "mqtt_host";
private static final int EXAMPLE_MQTT_PORT = 1883;
private static final String EXAMPLE_MQTT_USERNAME = "mqtt_username";
private static final String EXAMPLE_MQTT_PASSWORD = "mqtt_password";
private static final boolean EXAMPLE_MQTT_USE_TLS = true; // set to true
  1. In the same class, remove the telemetry and LwM2M parameters from the IoT3Mobility builder as they won't be used:
.telemetryParams(EXAMPLE_OTL_SCHEME,
                        EXAMPLE_OTL_HOST,
                        EXAMPLE_OTL_PORT,
                        EXAMPLE_OTL_ENDPOINT,
                        EXAMPLE_OTL_USERNAME,
                        EXAMPLE_OTL_PASSWORD)
.lwm2mParams(
                        EXAMPLE_LWM2M_CONFIG,
                        EXAMPLE_LWM2M_DEVICE,
                        lwm2mConnectivityStatistics)
  1. Run Iot3MobilityExample for a few seconds.
  2. Stop it.
  3. Modify the CpmVersion.V1_2_1 parameter in the sendTestCpm(...) method by CpmVersion.V2_1_1.
  4. Run Iot3MobilityExample for a few seconds.
  5. Stop it.

Expected results

  1. During the first run, you should see CPMs v1.2.1 being sent, received and processed (i.e. turned into RoadSensor and SensorObjects).
Sending CPM v1.2.1: CpmEnvelope121[type=cpm, origin=self, version=1.2.1, sourceUuid=ora_test_123, timestamp=1774262922794, message=CpmMessage121[protocolVersion=2, stationId=123456, generationDeltaTime=63018, managementContainer=ManagementContainer[stationType=15, referencePosition=ReferencePosition[latitude=486251520, longitude=22403490, altitude=0], confidence=ManagementConfidence[positionConfidenceEllipse=PositionConfidenceEllipse[semiMajorConfidence=4095, semiMinorConfidence=4095, semiMajorOrientation=3601], altitude=15]], stationDataContainer=StationDataContainer[originatingVehicleContainer=null, originatingRsuContainer=OriginatingRsuContainer[region=123, intersectionReferenceId=123, roadSegmentReferenceId=123]], sensorInformationContainer=SensorInformationContainer[sensorInformation=[SensorInformation[sensorId=123, type=4, detectionArea=DetectionArea[vehicleSensor=null, stationarySensorRadial=null, stationarySensorPolygon=null, stationarySensorCircular=AreaCircular[nodeCenterPoint=Offset[x=0, y=0, z=0], radius=200], stationarySensorEllipse=null, stationarySensorRectangle=null]]]], perceivedObjectContainer=PerceivedObjectContainer[perceivedObjects=[PerceivedObject[objectId=15, timeOfMeasurement=0, xDistance=-1807, yDistance=206, xSpeed=0, ySpeed=0, objectAge=1500, confidence=PerceivedObjectConfidence[xDistance=4095, yDistance=4095, xSpeed=0, ySpeed=0, object=15, zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, planarObjectDimension1=null, planarObjectDimension2=null, verticalObjectDimension=null, longitudinalLanePosition=null], zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, lowerTriangularCorrelationMatrixColumns=null, planarObjectDimension1=10, planarObjectDimension2=10, verticalObjectDimension=20, objectRefPoint=null, sensorIdList=[123], dynamicStatus=null, classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, singleVru=ObjectClassVru[pedestrian=1, bicyclist=null, motorcylist=null, animal=null], vruGroup=null, other=null], confidence=100]], matchedPosition=null], PerceivedObject[objectId=34, timeOfMeasurement=0, xDistance=1505, yDistance=93, xSpeed=0, ySpeed=0, objectAge=1500, confidence=PerceivedObjectConfidence[xDistance=4095, yDistance=4095, xSpeed=0, ySpeed=0, object=15, zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, planarObjectDimension1=null, planarObjectDimension2=null, verticalObjectDimension=null, longitudinalLanePosition=null], zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, lowerTriangularCorrelationMatrixColumns=null, planarObjectDimension1=20, planarObjectDimension2=20, verticalObjectDimension=15, objectRefPoint=null, sensorIdList=[123], dynamicStatus=null, classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, singleVru=ObjectClassVru[pedestrian=null, bicyclist=1, motorcylist=null, animal=null], vruGroup=null, other=null], confidence=100]], matchedPosition=null]]], freeSpaceAddendumContainer=null]]

// JSON messages received
Raw message received: {"type":"cpm","origin":"self","version":"1.2.1","source_uuid":"ora_test_123","timestamp":1774262922794,"message":{"protocol_version":2,"station_id":123456,"generation_delta_time":63018,"management_container":{"station_type":15,"reference_position":{"latitude":486251520,"longitude":22403490,"altitude":0},"confidence":{"position_confidence_ellipse":{"semi_major_confidence":4095,"semi_minor_confidence":4095,"semi_major_orientation":3601},"altitude":15}},"station_data_container":{"originating_rsu_container":{"region":123,"intersection_reference_id":123,"road_segment_reference_id":123}},"sensor_information_container":[{"sensor_id":123,"type":4,"detection_area":{"stationary_sensor_circular":{"node_center_point":{"x":0,"y":0,"z":0},"radius":200}}}],"perceived_object_container":[{"object_id":15,"time_of_measurement":0,"x_distance":-1807,"y_distance":206,"x_speed":0,"y_speed":0,"planar_object_dimension_1":10,"planar_object_dimension_2":10,"vertical_object_dimension":20,"object_age":1500,"sensor_id_list":[123],"classification":[{"object_class":{"single_vru":{"pedestrian":1}},"confidence":100}],"confidence":{"x_distance":4095,"y_distance":4095,"x_speed":0,"y_speed":0,"object":15}},{"object_id":34,"time_of_measurement":0,"x_distance":1505,"y_distance":93,"x_speed":0,"y_speed":0,"planar_object_dimension_1":20,"planar_object_dimension_2":20,"vertical_object_dimension":15,"object_age":1500,"sensor_id_list":[123],"classification":[{"object_class":{"single_vru":{"bicyclist":1}},"confidence":100}],"confidence":{"x_distance":4095,"y_distance":4095,"x_speed":0,"y_speed":0,"object":15}}]}}

// CPM version detected
Raw CPM v1.2.1: CpmEnvelope121[type=cpm, origin=self, version=1.2.1, sourceUuid=ora_test_123, timestamp=1774262922794, message=CpmMessage121[protocolVersion=2, stationId=123456, generationDeltaTime=63018, managementContainer=ManagementContainer[stationType=15, referencePosition=ReferencePosition[latitude=486251520, longitude=22403490, altitude=0], confidence=ManagementConfidence[positionConfidenceEllipse=PositionConfidenceEllipse[semiMajorConfidence=4095, semiMinorConfidence=4095, semiMajorOrientation=3601], altitude=15]], stationDataContainer=StationDataContainer[originatingVehicleContainer=null, originatingRsuContainer=OriginatingRsuContainer[region=123, intersectionReferenceId=123, roadSegmentReferenceId=123]], sensorInformationContainer=SensorInformationContainer[sensorInformation=[SensorInformation[sensorId=123, type=4, detectionArea=DetectionArea[vehicleSensor=null, stationarySensorRadial=null, stationarySensorPolygon=null, stationarySensorCircular=AreaCircular[nodeCenterPoint=Offset[x=0, y=0, z=0], radius=200], stationarySensorEllipse=null, stationarySensorRectangle=null]]]], perceivedObjectContainer=PerceivedObjectContainer[perceivedObjects=[PerceivedObject[objectId=15, timeOfMeasurement=0, xDistance=-1807, yDistance=206, xSpeed=0, ySpeed=0, objectAge=1500, confidence=PerceivedObjectConfidence[xDistance=4095, yDistance=4095, xSpeed=0, ySpeed=0, object=15, zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, planarObjectDimension1=null, planarObjectDimension2=null, verticalObjectDimension=null, longitudinalLanePosition=null], zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, lowerTriangularCorrelationMatrixColumns=null, planarObjectDimension1=10, planarObjectDimension2=10, verticalObjectDimension=20, objectRefPoint=null, sensorIdList=[123], dynamicStatus=null, classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, singleVru=ObjectClassVru[pedestrian=1, bicyclist=null, motorcylist=null, animal=null], vruGroup=null, other=null], confidence=100]], matchedPosition=null], PerceivedObject[objectId=34, timeOfMeasurement=0, xDistance=1505, yDistance=93, xSpeed=0, ySpeed=0, objectAge=1500, confidence=PerceivedObjectConfidence[xDistance=4095, yDistance=4095, xSpeed=0, ySpeed=0, object=15, zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, planarObjectDimension1=null, planarObjectDimension2=null, verticalObjectDimension=null, longitudinalLanePosition=null], zDistance=null, zSpeed=null, xAcceleration=null, yAcceleration=null, zAcceleration=null, rollAngle=null, pitchAngle=null, yawAngle=null, rollRate=null, pitchRate=null, yawRate=null, rollAcceleration=null, pitchAcceleration=null, yawAcceleration=null, lowerTriangularCorrelationMatrixColumns=null, planarObjectDimension1=20, planarObjectDimension2=20, verticalObjectDimension=15, objectRefPoint=null, sensorIdList=[123], dynamicStatus=null, classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, singleVru=ObjectClassVru[pedestrian=null, bicyclist=1, motorcylist=null, animal=null], vruGroup=null, other=null], confidence=100]], matchedPosition=null]]], freeSpaceAddendumContainer=null]]

// processed objects
Sensor Object update: ora_test_123_123456_15
Sensor Object update: ora_test_123_123456_34
Road Sensor update: ora_test_123_123456
  1. During the second run, you should see the exact same with CPMs v2.1.1.
Sending CPM v2.1.1: CpmEnvelope211[messageType=cpm, sourceUuid=ora_test_123, timestamp=1774262970264, version=2.1.1, objectIdRotationCount=null, message=CpmMessage211[protocolVersion=2, stationId=123456, managementContainer=ManagementContainer[referenceTime=701347770264, referencePosition=ReferencePosition[latitude=486251520, longitude=22403490, positionConfidenceEllipse=PositionConfidenceEllipse[semiMajor=4095, semiMinor=4095, semiMajorOrientation=3601], altitude=Altitude[value=0, confidence=15]], segmentationInfo=null, messageRateRange=null], originatingVehicleContainer=null, originatingRsuContainer=OriginatingRsuContainer[mapReferences=[MapReference[roadSegment=RoadSegment[id=123, region=123], intersection=null]]], sensorInformationContainer=SensorInformationContainer[sensorInformation=[SensorInformation[sensorId=123, sensorType=4, perceptionRegionShape=Shape[rectangular=null, circular=Circular[radius=200, shapeReferencePoint=CartesianPosition3d[xCoordinate=0, yCoordinate=0, zCoordinate=null], height=null], polygonal=null, elliptical=null, radial=null, radialShapes=null], perceptionRegionConfidence=100, shadowingApplies=true]]], perceptionRegionContainer=null, perceivedObjectContainer=PerceivedObjectContainer[perceivedObjects=[PerceivedObject[measurementDeltaTime=0, position=CartesianPosition3dWithConfidence[xCoordinate=CartesianCoordinateWithConfidence[value=-1797, confidence=100], yCoordinate=CartesianCoordinateWithConfidence[value=208, confidence=100], zCoordinate=null], objectId=15, velocity=null, acceleration=null, angles=null, zAngularVelocity=null, lowerTriangularCorrelationMatrices=null, objectDimensionZ=null, objectDimensionY=null, objectDimensionX=null, objectAge=1500, objectPerceptionQuality=null, sensorIdList=[123], classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, vru=ObjectClassVru[pedestrian=1, bicyclistAndLightVruVehicle=null, motorcylist=null, animal=null], group=null, other=null], confidence=100]], mapPosition=null], PerceivedObject[measurementDeltaTime=0, position=CartesianPosition3dWithConfidence[xCoordinate=CartesianCoordinateWithConfidence[value=1504, confidence=100], yCoordinate=CartesianCoordinateWithConfidence[value=110, confidence=100], zCoordinate=null], objectId=34, velocity=null, acceleration=null, angles=null, zAngularVelocity=null, lowerTriangularCorrelationMatrices=null, objectDimensionZ=null, objectDimensionY=null, objectDimensionX=null, objectAge=1500, objectPerceptionQuality=null, sensorIdList=[123], classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, vru=ObjectClassVru[pedestrian=null, bicyclistAndLightVruVehicle=1, motorcylist=null, animal=null], group=null, other=null], confidence=100]], mapPosition=null]]]]]

// JSON message received
Raw message received: {"message_type":"cpm","source_uuid":"ora_test_123","timestamp":1774262970264,"version":"2.1.1","message":{"protocol_version":2,"station_id":123456,"management_container":{"reference_time":701347770264,"reference_position":{"latitude":486251520,"longitude":22403490,"position_confidence_ellipse":{"semi_major":4095,"semi_minor":4095,"semi_major_orientation":3601},"altitude":{"value":0,"confidence":15}}},"originating_rsu_container":[{"road_segment":{"id":123,"region":123}}],"sensor_information_container":[{"sensor_id":123,"sensor_type":4,"perception_region_shape":{"circular":{"radius":200,"shape_reference_point":{"x_coordinate":0,"y_coordinate":0}}},"perception_region_confidence":100,"shadowing_applies":true}],"perceived_object_container":[{"measurement_delta_time":0,"position":{"x_coordinate":{"value":-1797,"confidence":100},"y_coordinate":{"value":208,"confidence":100}},"object_id":15,"object_age":1500,"sensor_id_list":[123],"classification":[{"object_class":{"vru":{"pedestrian":1}},"confidence":100}]},{"measurement_delta_time":0,"position":{"x_coordinate":{"value":1504,"confidence":100},"y_coordinate":{"value":110,"confidence":100}},"object_id":34,"object_age":1500,"sensor_id_list":[123],"classification":[{"object_class":{"vru":{"bicyclist_and_light_vru_vehicle":1}},"confidence":100}]}]}}

// CPM version detected
Raw CPM v2.1.1: CpmEnvelope211[messageType=cpm, sourceUuid=ora_test_123, timestamp=1774262970264, version=2.1.1, objectIdRotationCount=null, message=CpmMessage211[protocolVersion=2, stationId=123456, managementContainer=ManagementContainer[referenceTime=701347770264, referencePosition=ReferencePosition[latitude=486251520, longitude=22403490, positionConfidenceEllipse=PositionConfidenceEllipse[semiMajor=4095, semiMinor=4095, semiMajorOrientation=3601], altitude=Altitude[value=0, confidence=15]], segmentationInfo=null, messageRateRange=null], originatingVehicleContainer=null, originatingRsuContainer=OriginatingRsuContainer[mapReferences=[MapReference[roadSegment=RoadSegment[id=123, region=123], intersection=null]]], sensorInformationContainer=SensorInformationContainer[sensorInformation=[SensorInformation[sensorId=123, sensorType=4, perceptionRegionShape=Shape[rectangular=null, circular=Circular[radius=200, shapeReferencePoint=CartesianPosition3d[xCoordinate=0, yCoordinate=0, zCoordinate=null], height=null], polygonal=null, elliptical=null, radial=null, radialShapes=null], perceptionRegionConfidence=100, shadowingApplies=true]]], perceptionRegionContainer=null, perceivedObjectContainer=PerceivedObjectContainer[perceivedObjects=[PerceivedObject[measurementDeltaTime=0, position=CartesianPosition3dWithConfidence[xCoordinate=CartesianCoordinateWithConfidence[value=-1797, confidence=100], yCoordinate=CartesianCoordinateWithConfidence[value=208, confidence=100], zCoordinate=null], objectId=15, velocity=null, acceleration=null, angles=null, zAngularVelocity=null, lowerTriangularCorrelationMatrices=null, objectDimensionZ=null, objectDimensionY=null, objectDimensionX=null, objectAge=1500, objectPerceptionQuality=null, sensorIdList=[123], classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, vru=ObjectClassVru[pedestrian=1, bicyclistAndLightVruVehicle=null, motorcylist=null, animal=null], group=null, other=null], confidence=100]], mapPosition=null], PerceivedObject[measurementDeltaTime=0, position=CartesianPosition3dWithConfidence[xCoordinate=CartesianCoordinateWithConfidence[value=1504, confidence=100], yCoordinate=CartesianCoordinateWithConfidence[value=110, confidence=100], zCoordinate=null], objectId=34, velocity=null, acceleration=null, angles=null, zAngularVelocity=null, lowerTriangularCorrelationMatrices=null, objectDimensionZ=null, objectDimensionY=null, objectDimensionX=null, objectAge=1500, objectPerceptionQuality=null, sensorIdList=[123], classification=[ObjectClassification[objectClass=ObjectClass[vehicle=null, vru=ObjectClassVru[pedestrian=null, bicyclistAndLightVruVehicle=1, motorcylist=null, animal=null], group=null, other=null], confidence=100]], mapPosition=null]]]]]

// processed objects
Sensor Object update: ora_test_123_123456_15
Sensor Object update: ora_test_123_123456_34
Road Sensor update: ora_test_123_123456

Note: it is normal if source_uuid, station_id and timestamps differ in your test.

…M v2.1.1

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Including methods to derive perceived object speed and heading from their xSpeed and ySpeed parameters.

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
…2.1.1

RoadSensor, SensorObject and RoadSensorManager and its callback have been updated.
A new SensorObjectType has been added as a simplified abstraction of CPM's ObjectClass.

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
…ample

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
…bjectConfidence builders

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
Added a factory class for each CPM version, to avoid messy imports in the main example classes.

Aligned CAM version selection location with CPM one for example classes.

Signed-off-by: Mathieu LEFEBVRE <mathieu1.lefebvre@orange.com>
@mathieu1fb mathieu1fb added this to the Sprint 19 milestone Mar 23, 2026
@mathieu1fb mathieu1fb requested a review from fredOG-2A1 March 23, 2026 10:54
@mathieu1fb mathieu1fb self-assigned this Mar 23, 2026
Copy link
Member

Choose a reason for hiding this comment

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

As I'm always a newbie with english, I'm not comfortable with this sentence:
Throws if the version is not 1.2.1.
Maybe add the name of the exception? (Throws CpmException if the version is not 1.2.1.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure which line you are referring to, but when using a parser for a specific CPM version, we indeed want to throw an error if a different version was provided to the parser.

Copy link
Member

@fredOG-2A1 fredOG-2A1 left a comment

Choose a reason for hiding this comment

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

A few remarks about cosmetics:

I found a lot a //TODO, if you plan to not fill them, maybe remove them?
Maybe give a name to what is thrown in comments?

Another remark:

I read the word 'cam' in this cpm refactoring.

Starting tests.

@mathieu1fb
Copy link
Collaborator Author

A few remarks about cosmetics:

I found a lot a //TODO, if you plan to not fill them, maybe remove them?
Maybe give a name to what is thrown in comments?

Another remark:

I read the word 'cam' in this cpm refactoring.

Starting tests.

Thanks for the code review! However I'm pretty sure that you're not looking at the latest commit of the PR, so I doubt that the tests will succeed ;)

@fredOG-2A1
Copy link
Member

Got it.
I rewieved here:
[mathieu1fb] added 3 commits [2 weeks ago]

A lot of job must have be done...

I'm going to read more recent code.

Copy link
Member

@fredOG-2A1 fredOG-2A1 left a comment

Choose a reason for hiding this comment

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

OK, right track now.
We can forget all my previous comments.
About to do the tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Refactor CPM v1.2.1 Add support for CPM v2.1.1

2 participants