ocpp-rails 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +19 -6
- data/app/jobs/ocpp/rails/session_async_hook_job.rb +25 -0
- data/app/models/ocpp/rails/charge_point.rb +21 -1
- data/app/models/ocpp/rails/connector_status.rb +16 -0
- data/app/services/ocpp/rails/actions/start_transaction_handler.rb +6 -2
- data/app/services/ocpp/rails/actions/status_notification_handler.rb +3 -7
- data/app/services/ocpp/rails/actions/stop_transaction_handler.rb +7 -4
- data/app/services/ocpp/rails/connector_status_backfill.rb +37 -0
- data/app/services/ocpp/rails/session_hook_manager.rb +28 -0
- data/db/migrate/20260711000000_create_ocpp_connector_statuses.rb +13 -0
- data/db/migrate/20260711000001_backfill_ocpp_connector_statuses.rb +9 -0
- data/lib/generators/ocpp/rails/install_generator.rb +2 -0
- data/lib/generators/ocpp/rails/templates/create_ocpp_connector_statuses.rb +13 -0
- data/lib/generators/ocpp/rails/templates/ocpp_rails.rb +7 -0
- data/lib/ocpp/rails/version.rb +1 -1
- data/lib/ocpp/rails.rb +9 -1
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42e87a1079adc1b8d7641883fa8e57b21d07ff38a5b150dd231a64d637d37a47
|
|
4
|
+
data.tar.gz: f4fd46249622b68f30f035c99da715913219940eabb2dabdefc8e15255840a84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b103ecc7ed7e89acdd7aad80e93314cffe57f8c3fd84664a3cbd8da00e10a60f0a7e2b8c03870b27f58ee2fbf93e89b09f020e88db274a776a79d32b6a5f6f4f
|
|
7
|
+
data.tar.gz: 3880fb4073c68ece89623df6740b529684c7a71f284dab18b4080528418ba31d339cf00565de3039d867ec0e6d757322e26b4970ba73d27425dfc433e93cdb8d
|
data/README.md
CHANGED
|
@@ -24,7 +24,8 @@ OCPP message layer.
|
|
|
24
24
|
### OCPP Protocol Layer (What This Gem Provides)
|
|
25
25
|
- 📡 **WebSocket Communication** - ActionCable channel handles bidirectional OCPP messages
|
|
26
26
|
- 🔌 **Protocol Handlers** - BootNotification, Authorize, Heartbeat, StartTransaction, StopTransaction, MeterValues, StatusNotification
|
|
27
|
-
- 🗄️ **Data Models** - ChargePoint, ChargingSession, MeterValue, Message (audit log)
|
|
27
|
+
- 🗄️ **Data Models** - ChargePoint, ConnectorStatus, ChargingSession, MeterValue, Message (audit log)
|
|
28
|
+
- 🪝 **Lifecycle Hooks** - authorization, state-change, and session-start/stop hooks (sync or async via ActiveJob) — react without polling
|
|
28
29
|
- 🚀 **Remote Control Jobs** - RemoteStartTransaction, RemoteStopTransaction, UnlockConnector, Reset, ClearCache, GetConfiguration, ChangeConfiguration, ChangeAvailability
|
|
29
30
|
- 📊 **Real-time Broadcasts** - ActionCable broadcasts for status, sessions, and meter values
|
|
30
31
|
- 💾 **SQLite Compatible** - Works with async adapter, no Redis required for development
|
|
@@ -40,7 +41,7 @@ OCPP message layer.
|
|
|
40
41
|
- ✅ **Core session flow** — inbound BootNotification, Authorize, Heartbeat, Start/StopTransaction, MeterValues, StatusNotification
|
|
41
42
|
- ✅ **Remote Control** — RemoteStartTransaction, RemoteStopTransaction, UnlockConnector (delivery + end-to-end flow, tested)
|
|
42
43
|
- ✅ **Message Audit** — every inbound/outbound frame logged for debugging and compliance
|
|
43
|
-
- ✅ **Multi-connector** — one active session per connector, enforced at the DB level
|
|
44
|
+
- ✅ **Multi-connector** — one active session per connector, enforced at the DB level; per-connector status tracked independently of whole-station status
|
|
44
45
|
- 🚧 **Everything else** — see the honest, per-test-case [OCPP 1.6 Compliance Status](#-ocpp-16-compliance-status) below
|
|
45
46
|
|
|
46
47
|
## 📋 OCPP 1.6 Compliance Status
|
|
@@ -157,17 +158,25 @@ and the [Security Guide](docs/security.md).
|
|
|
157
158
|
|
|
158
159
|
### Monitor Charge Point Status
|
|
159
160
|
|
|
161
|
+
`ChargePoint#status` is the whole-station status (from connector-0
|
|
162
|
+
StatusNotifications: Available/Unavailable/Faulted) — it says nothing about
|
|
163
|
+
any individual connector once a station has more than one.
|
|
164
|
+
|
|
160
165
|
```ruby
|
|
161
166
|
# Query charge points
|
|
162
167
|
connected_cps = Ocpp::Rails::ChargePoint.connected
|
|
163
168
|
available_cps = Ocpp::Rails::ChargePoint.available
|
|
164
|
-
charging_cps = Ocpp::Rails::ChargePoint.charging
|
|
169
|
+
charging_cps = Ocpp::Rails::ChargePoint.charging # any active session, any connector
|
|
165
170
|
|
|
166
171
|
# Check specific charge point
|
|
167
172
|
cp = Ocpp::Rails::ChargePoint.find_by(identifier: "CP001")
|
|
168
173
|
cp.connected? # => true/false
|
|
169
|
-
cp.status # => "Available", "
|
|
174
|
+
cp.status # => whole-station status: "Available", "Unavailable", "Faulted"
|
|
170
175
|
cp.last_heartbeat_at
|
|
176
|
+
|
|
177
|
+
# Per-connector status (independent of any other connector on the same station)
|
|
178
|
+
cp.connector_status(1) # => "Available", "Charging", "SuspendedEV", ...
|
|
179
|
+
cp.connector_charging?(1) # => true/false, derived from active sessions
|
|
171
180
|
```
|
|
172
181
|
|
|
173
182
|
### Monitor Active Sessions
|
|
@@ -269,8 +278,12 @@ For complete implementation examples, see the [Remote Charging Guide](docs/remot
|
|
|
269
278
|
|
|
270
279
|
- **`Ocpp::Rails::ChargePoint`** - Represents a physical charging station
|
|
271
280
|
- Tracks connection status, firmware version, vendor info
|
|
272
|
-
- Has many charging sessions and meter values
|
|
273
|
-
- Provides status scopes (connected, available, charging)
|
|
281
|
+
- Has many charging sessions, connector statuses, and meter values
|
|
282
|
+
- Provides status scopes (connected, available, charging) and per-connector reads (`connector_status`, `connector_charging?`)
|
|
283
|
+
|
|
284
|
+
- **`Ocpp::Rails::ConnectorStatus`** - Last reported status for one connector
|
|
285
|
+
- One row per `(charge_point, connector_id)`, written by StatusNotification
|
|
286
|
+
- Independent of whole-station status and of any other connector's activity
|
|
274
287
|
|
|
275
288
|
- **`Ocpp::Rails::ChargingSession`** - Represents a charging transaction
|
|
276
289
|
- Manages session lifecycle (active/completed)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Ocpp
|
|
2
|
+
module Rails
|
|
3
|
+
class SessionAsyncHookJob < ApplicationJob
|
|
4
|
+
queue_as :ocpp_hooks
|
|
5
|
+
|
|
6
|
+
retry_on StandardError, wait: :polynomially_longer, attempts: 3
|
|
7
|
+
|
|
8
|
+
def perform(charging_session_id, event, hook_class_name)
|
|
9
|
+
charging_session = Ocpp::Rails::ChargingSession.find_by(id: charging_session_id)
|
|
10
|
+
|
|
11
|
+
unless charging_session
|
|
12
|
+
::Rails.logger.warn("ChargingSession #{charging_session_id} not found, may have been cleaned up")
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
hook_class = hook_class_name.constantize
|
|
17
|
+
hook = hook_class.new
|
|
18
|
+
hook.call(charging_session, event)
|
|
19
|
+
rescue => error
|
|
20
|
+
::Rails.logger.error("SessionAsyncHook #{hook_class_name} failed for ChargingSession #{charging_session_id} (#{event}): #{error.message}")
|
|
21
|
+
raise
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -4,6 +4,7 @@ module Ocpp
|
|
|
4
4
|
self.table_name = "ocpp_charge_points"
|
|
5
5
|
|
|
6
6
|
has_many :charging_sessions, dependent: :destroy, class_name: "Ocpp::Rails::ChargingSession"
|
|
7
|
+
has_many :connector_statuses, dependent: :destroy, class_name: "Ocpp::Rails::ConnectorStatus"
|
|
7
8
|
has_many :meter_values, dependent: :destroy, class_name: "Ocpp::Rails::MeterValue"
|
|
8
9
|
has_many :messages, dependent: :destroy, class_name: "Ocpp::Rails::Message"
|
|
9
10
|
has_many :state_changes, dependent: :destroy, class_name: "Ocpp::Rails::StateChange"
|
|
@@ -14,7 +15,9 @@ module Ocpp
|
|
|
14
15
|
|
|
15
16
|
scope :connected, -> { where(connected: true) }
|
|
16
17
|
scope :available, -> { where(status: "Available") }
|
|
17
|
-
|
|
18
|
+
# Charging is a fact about sessions, not about ChargePoint#status
|
|
19
|
+
# (which only ever holds connector-0 values: Available/Unavailable/Faulted).
|
|
20
|
+
scope :charging, -> { joins(:charging_sessions).merge(Ocpp::Rails::ChargingSession.active).distinct }
|
|
18
21
|
|
|
19
22
|
# Stores the station's Basic Auth password as a SHA-256 digest.
|
|
20
23
|
# OCPP-J passwords are high-entropy machine credentials (the spec
|
|
@@ -64,6 +67,23 @@ module Ocpp
|
|
|
64
67
|
def available?
|
|
65
68
|
status == "Available" && connected?
|
|
66
69
|
end
|
|
70
|
+
|
|
71
|
+
# Last status the station reported for this connector via
|
|
72
|
+
# StatusNotification; nil until the connector has reported once.
|
|
73
|
+
def connector_status(connector_id)
|
|
74
|
+
connector_statuses.find_by(connector_id: connector_id)&.status
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def connector_error_code(connector_id)
|
|
78
|
+
connector_statuses.find_by(connector_id: connector_id)&.error_code
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Whether a transaction is running on the connector. Authoritative by
|
|
82
|
+
# definition, unlike connector_status which is only as fresh as the
|
|
83
|
+
# station's last StatusNotification.
|
|
84
|
+
def connector_charging?(connector_id)
|
|
85
|
+
charging_sessions.active.where(connector_id: connector_id).exists?
|
|
86
|
+
end
|
|
67
87
|
end
|
|
68
88
|
end
|
|
69
89
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Ocpp
|
|
2
|
+
module Rails
|
|
3
|
+
class ConnectorStatus < ApplicationRecord
|
|
4
|
+
self.table_name = "ocpp_connector_statuses"
|
|
5
|
+
|
|
6
|
+
belongs_to :charge_point, class_name: "Ocpp::Rails::ChargePoint"
|
|
7
|
+
|
|
8
|
+
# Connector 0 (the charge point main controller) is tracked on
|
|
9
|
+
# ChargePoint#status, never here.
|
|
10
|
+
validates :connector_id, presence: true,
|
|
11
|
+
numericality: { greater_than_or_equal_to: 1, only_integer: true },
|
|
12
|
+
uniqueness: { scope: :charge_point_id }
|
|
13
|
+
validates :status, presence: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -51,8 +51,12 @@ module Ocpp
|
|
|
51
51
|
|
|
52
52
|
::Rails.logger.info("[OCPP] StartTransaction from #{@charge_point.identifier}: Connector #{@payload['connectorId']}, Transaction ID: #{session.transaction_id}")
|
|
53
53
|
|
|
54
|
-
#
|
|
55
|
-
|
|
54
|
+
# Connector status is owned by StatusNotification (OCPP 1.6); a
|
|
55
|
+
# transaction starting says nothing about ChargePoint#status.
|
|
56
|
+
|
|
57
|
+
# Duplicate/replayed StartTransactions resume the open session
|
|
58
|
+
# above and intentionally do not re-fire this hook.
|
|
59
|
+
Ocpp::Rails::SessionHookManager.execute_hooks(session, "started")
|
|
56
60
|
|
|
57
61
|
# Broadcast session started event for real-time UI updates
|
|
58
62
|
broadcast_session_started(session)
|
|
@@ -43,13 +43,9 @@ module Ocpp
|
|
|
43
43
|
private
|
|
44
44
|
|
|
45
45
|
def update_connector_status(connector_id, status, error_code)
|
|
46
|
-
|
|
47
|
-
old_status =
|
|
48
|
-
|
|
49
|
-
metadata["connector_#{connector_id}_status"] = status
|
|
50
|
-
metadata["connector_#{connector_id}_error_code"] = error_code if error_code
|
|
51
|
-
metadata["connector_#{connector_id}_updated_at"] = Time.current.iso8601
|
|
52
|
-
@charge_point.update(metadata: metadata)
|
|
46
|
+
record = @charge_point.connector_statuses.find_or_initialize_by(connector_id: connector_id)
|
|
47
|
+
old_status = record.status
|
|
48
|
+
record.update!(status: status, error_code: error_code)
|
|
53
49
|
|
|
54
50
|
# Log state change if status actually changed
|
|
55
51
|
log_status_change(connector_id, old_status, status, {
|
|
@@ -34,10 +34,13 @@ module Ocpp
|
|
|
34
34
|
process_transaction_data(session, @payload["transactionData"])
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
# Connector status is owned by StatusNotification (OCPP 1.6); the
|
|
38
|
+
# station reports Finishing/Available itself after the transaction.
|
|
39
|
+
|
|
40
|
+
# Fired after transactionData so hooks see the Transaction.End
|
|
41
|
+
# meter values. Stations recovering from an offline period may
|
|
42
|
+
# still deliver queued MeterValues after this point.
|
|
43
|
+
Ocpp::Rails::SessionHookManager.execute_hooks(session, "stopped")
|
|
41
44
|
|
|
42
45
|
# Broadcast session stopped event for real-time UI updates
|
|
43
46
|
broadcast_session_stopped(session)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Ocpp
|
|
2
|
+
module Rails
|
|
3
|
+
# Moves per-connector status from the pre-0.3.0 metadata convention
|
|
4
|
+
# (connector_<n>_status / _error_code / _updated_at keys on
|
|
5
|
+
# ChargePoint#metadata) into the ocpp_connector_statuses table.
|
|
6
|
+
# Idempotent; rows the station has written since the upgrade win over
|
|
7
|
+
# legacy metadata.
|
|
8
|
+
module ConnectorStatusBackfill
|
|
9
|
+
LEGACY_KEY = /\Aconnector_(\d+)_(status|error_code|updated_at)\z/
|
|
10
|
+
|
|
11
|
+
def self.run
|
|
12
|
+
Ocpp::Rails::ChargePoint.find_each do |charge_point|
|
|
13
|
+
metadata = charge_point.metadata || {}
|
|
14
|
+
legacy = metadata.select { |key, _| key.to_s.match?(LEGACY_KEY) }
|
|
15
|
+
next if legacy.empty?
|
|
16
|
+
|
|
17
|
+
connector_ids(legacy).each do |connector_id|
|
|
18
|
+
status = legacy["connector_#{connector_id}_status"]
|
|
19
|
+
next unless status
|
|
20
|
+
|
|
21
|
+
record = charge_point.connector_statuses.find_or_initialize_by(connector_id: connector_id)
|
|
22
|
+
next if record.persisted?
|
|
23
|
+
|
|
24
|
+
record.update!(status: status, error_code: legacy["connector_#{connector_id}_error_code"])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
charge_point.update!(metadata: metadata.except(*legacy.keys))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.connector_ids(legacy)
|
|
32
|
+
legacy.keys.filter_map { |key| key.to_s[LEGACY_KEY, 1]&.to_i }.uniq
|
|
33
|
+
end
|
|
34
|
+
private_class_method :connector_ids
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Ocpp
|
|
2
|
+
module Rails
|
|
3
|
+
module SessionHookManager
|
|
4
|
+
EVENTS = [ "started", "stopped" ].freeze
|
|
5
|
+
|
|
6
|
+
def self.execute_hooks(charging_session, event)
|
|
7
|
+
hooks = Ocpp::Rails.configuration.session_hooks
|
|
8
|
+
|
|
9
|
+
hooks.each do |hook|
|
|
10
|
+
begin
|
|
11
|
+
if hook.respond_to?(:async?) && hook.async?
|
|
12
|
+
# Enqueue async hook job
|
|
13
|
+
Ocpp::Rails::SessionAsyncHookJob.perform_later(charging_session.id, event, hook.class.name)
|
|
14
|
+
else
|
|
15
|
+
# Execute synchronously
|
|
16
|
+
hook.call(charging_session, event)
|
|
17
|
+
end
|
|
18
|
+
rescue => error
|
|
19
|
+
::Rails.logger.error("SessionHook #{hook.class.name} failed: #{error.message}")
|
|
20
|
+
::Rails.logger.error(error.backtrace.join("\n"))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateOcppConnectorStatuses < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :ocpp_connector_statuses do |t|
|
|
4
|
+
t.references :charge_point, null: false, foreign_key: { to_table: :ocpp_charge_points }
|
|
5
|
+
t.integer :connector_id, null: false
|
|
6
|
+
t.string :status, null: false
|
|
7
|
+
t.string :error_code
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :ocpp_connector_statuses, [ :charge_point_id, :connector_id ], unique: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -23,6 +23,8 @@ module Ocpp
|
|
|
23
23
|
migration_template "create_ocpp_meter_values.rb", "db/migrate/create_ocpp_meter_values.rb"
|
|
24
24
|
sleep 1
|
|
25
25
|
migration_template "create_ocpp_messages.rb", "db/migrate/create_ocpp_messages.rb"
|
|
26
|
+
sleep 1
|
|
27
|
+
migration_template "create_ocpp_connector_statuses.rb", "db/migrate/create_ocpp_connector_statuses.rb"
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def mount_engine
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateOcppConnectorStatuses < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :ocpp_connector_statuses do |t|
|
|
4
|
+
t.references :charge_point, null: false, foreign_key: { to_table: :ocpp_charge_points }
|
|
5
|
+
t.integer :connector_id, null: false
|
|
6
|
+
t.string :status, null: false
|
|
7
|
+
t.string :error_code
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :ocpp_connector_statuses, [ :charge_point_id, :connector_id ], unique: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -10,4 +10,11 @@ Ocpp::Rails.setup do |config|
|
|
|
10
10
|
|
|
11
11
|
# Connection timeout in seconds
|
|
12
12
|
config.connection_timeout = 30
|
|
13
|
+
|
|
14
|
+
# Session lifecycle hooks: objects responding to
|
|
15
|
+
# call(charging_session, event) with event "started" or "stopped".
|
|
16
|
+
# Define async? => true on a hook to run it via ActiveJob instead of
|
|
17
|
+
# inline. Example:
|
|
18
|
+
#
|
|
19
|
+
# config.register_session_hook(MySessionHook.new)
|
|
13
20
|
end
|
data/lib/ocpp/rails/version.rb
CHANGED
data/lib/ocpp/rails.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Ocpp
|
|
|
36
36
|
attr_accessor :ocpp_version, :supported_versions, :heartbeat_interval, :connection_timeout,
|
|
37
37
|
:state_change_hooks, :state_change_retention_days, :state_change_cleanup_enabled,
|
|
38
38
|
:authorization_hooks, :authorization_retention_days, :authorization_cleanup_enabled,
|
|
39
|
-
:implausible_energy_jump_wh, :authentication_mode,
|
|
39
|
+
:session_hooks, :implausible_energy_jump_wh, :authentication_mode,
|
|
40
40
|
:max_messages_per_minute, :max_connection_attempts_per_minute
|
|
41
41
|
|
|
42
42
|
def initialize
|
|
@@ -47,6 +47,7 @@ module Ocpp
|
|
|
47
47
|
@connection_timeout = 30
|
|
48
48
|
@state_change_hooks = []
|
|
49
49
|
@authorization_hooks = []
|
|
50
|
+
@session_hooks = []
|
|
50
51
|
@state_change_retention_days = 30
|
|
51
52
|
@state_change_cleanup_enabled = true
|
|
52
53
|
@authorization_retention_days = 30
|
|
@@ -77,6 +78,13 @@ module Ocpp
|
|
|
77
78
|
end
|
|
78
79
|
@authorization_hooks << hook
|
|
79
80
|
end
|
|
81
|
+
|
|
82
|
+
def register_session_hook(hook)
|
|
83
|
+
unless hook.respond_to?(:call)
|
|
84
|
+
raise ArgumentError, "Hook must respond to :call method"
|
|
85
|
+
end
|
|
86
|
+
@session_hooks << hook
|
|
87
|
+
end
|
|
80
88
|
end
|
|
81
89
|
end
|
|
82
90
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocpp-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakob Sommerhuber
|
|
@@ -119,12 +119,14 @@ files:
|
|
|
119
119
|
- app/jobs/ocpp/rails/remote_start_transaction_job.rb
|
|
120
120
|
- app/jobs/ocpp/rails/remote_stop_transaction_job.rb
|
|
121
121
|
- app/jobs/ocpp/rails/reset_job.rb
|
|
122
|
+
- app/jobs/ocpp/rails/session_async_hook_job.rb
|
|
122
123
|
- app/jobs/ocpp/rails/unlock_connector_job.rb
|
|
123
124
|
- app/mailers/ocpp/rails/application_mailer.rb
|
|
124
125
|
- app/models/ocpp/rails/application_record.rb
|
|
125
126
|
- app/models/ocpp/rails/authorization.rb
|
|
126
127
|
- app/models/ocpp/rails/charge_point.rb
|
|
127
128
|
- app/models/ocpp/rails/charging_session.rb
|
|
129
|
+
- app/models/ocpp/rails/connector_status.rb
|
|
128
130
|
- app/models/ocpp/rails/message.rb
|
|
129
131
|
- app/models/ocpp/rails/meter_value.rb
|
|
130
132
|
- app/models/ocpp/rails/state_change.rb
|
|
@@ -136,10 +138,12 @@ files:
|
|
|
136
138
|
- app/services/ocpp/rails/actions/status_notification_handler.rb
|
|
137
139
|
- app/services/ocpp/rails/actions/stop_transaction_handler.rb
|
|
138
140
|
- app/services/ocpp/rails/authorization_hook_manager.rb
|
|
141
|
+
- app/services/ocpp/rails/connector_status_backfill.rb
|
|
139
142
|
- app/services/ocpp/rails/message_handler.rb
|
|
140
143
|
- app/services/ocpp/rails/meter_anomaly_detector.rb
|
|
141
144
|
- app/services/ocpp/rails/protocol.rb
|
|
142
145
|
- app/services/ocpp/rails/rate_limiter.rb
|
|
146
|
+
- app/services/ocpp/rails/session_hook_manager.rb
|
|
143
147
|
- app/services/ocpp/rails/state_change_hook_manager.rb
|
|
144
148
|
- app/services/ocpp/rails/station_authenticator.rb
|
|
145
149
|
- app/services/ocpp/rails/timestamp_parser.rb
|
|
@@ -153,10 +157,13 @@ files:
|
|
|
153
157
|
- db/migrate/20260707000002_add_active_session_guard_to_ocpp_charging_sessions.rb
|
|
154
158
|
- db/migrate/20260707000003_add_anomaly_flags_to_ocpp_meter_values.rb
|
|
155
159
|
- db/migrate/20260707000004_add_auth_password_digest_to_ocpp_charge_points.rb
|
|
160
|
+
- db/migrate/20260711000000_create_ocpp_connector_statuses.rb
|
|
161
|
+
- db/migrate/20260711000001_backfill_ocpp_connector_statuses.rb
|
|
156
162
|
- lib/generators/ocpp/rails/install_generator.rb
|
|
157
163
|
- lib/generators/ocpp/rails/templates/README
|
|
158
164
|
- lib/generators/ocpp/rails/templates/create_ocpp_charge_points.rb
|
|
159
165
|
- lib/generators/ocpp/rails/templates/create_ocpp_charging_sessions.rb
|
|
166
|
+
- lib/generators/ocpp/rails/templates/create_ocpp_connector_statuses.rb
|
|
160
167
|
- lib/generators/ocpp/rails/templates/create_ocpp_messages.rb
|
|
161
168
|
- lib/generators/ocpp/rails/templates/create_ocpp_meter_values.rb
|
|
162
169
|
- lib/generators/ocpp/rails/templates/ocpp_rails.rb
|