Skip to content

Commit ee0c0e6

Browse files
committed
add file_select() for IEC 104 file transfer
1 parent f9de691 commit ee0c0e6

4 files changed

Lines changed: 110 additions & 0 deletions

File tree

c104/__init__.pyi

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,31 @@ class Connection:
933933
>>> if not my_connection.test(common_address=47):
934934
>>> raise ValueError("Cannot send test command")
935935
"""
936+
def file_select(self, common_address: int, ioa: int, wait_for_response: bool = True) -> bool:
937+
"""
938+
select a file on the remote terminal unit (server) for transfer
939+
sends F_SC_NA_1 (Type 122) with SCQ=1 (SELECT_FILE)
940+
server responds with F_FR_NA_1 (Type 120 - FILE_READY)
941+
942+
Parameters
943+
----------
944+
common_address: int
945+
station common address
946+
ioa: int
947+
information object address of the file
948+
wait_for_response: bool
949+
block call until response received?
950+
951+
Returns
952+
-------
953+
bool
954+
True, if file selection successful
955+
956+
Example
957+
-------
958+
>>> if my_connection.file_select(common_address=1, ioa=30000):
959+
>>> print("File selected successfully")
960+
"""
936961
def unmute(self) -> bool:
937962
"""
938963
tell the remote terminal unit (server) that this connection is not muted, allow monitoring messages

src/python.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,6 +2393,35 @@ Example
23932393
)def",
23942394
"common_address"_a, "with_time"_a = true,
23952395
"wait_for_response"_a = true, py::return_value_policy::copy)
2396+
.def(
2397+
"file_select", &Remote::Connection::fileSelect,
2398+
R"def(file_select(self: c104.Connection, common_address: int, ioa: int, wait_for_response: bool = True) -> bool
2399+
2400+
select a file on the remote terminal unit (server) for transfer
2401+
sends F_SC_NA_1 (Type 122) with SCQ=1 (SELECT_FILE)
2402+
server responds with F_FR_NA_1 (Type 120 - FILE_READY)
2403+
2404+
Parameters
2405+
----------
2406+
common_address: int
2407+
station common address
2408+
ioa: int
2409+
information object address of the file
2410+
wait_for_response: bool
2411+
block call until response received?
2412+
2413+
Returns
2414+
-------
2415+
bool
2416+
True, if file selection successful
2417+
2418+
Example
2419+
-------
2420+
>>> if my_connection.file_select(common_address=1, ioa=30000):
2421+
>>> print("File selected successfully")
2422+
)def",
2423+
"common_address"_a, "ioa"_a, "wait_for_response"_a = true,
2424+
py::return_value_policy::copy)
23962425
.def(
23972426
"add_station", &Remote::Connection::addStation,
23982427
R"def(add_station(self: c104.Connection, common_address: int) -> c104.Station | None

src/remote/Connection.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,51 @@ bool Connection::test(std::uint_fast16_t commonAddress, bool with_time,
932932
return result;
933933
}
934934

935+
bool Connection::fileSelect(std::uint_fast16_t commonAddress,
936+
std::uint_fast32_t ioa,
937+
const bool wait_for_response) {
938+
Module::ScopedGilRelease const scoped("Connection.fileSelect");
939+
940+
if (!isOpen())
941+
return false;
942+
943+
std::string const cmdId =
944+
std::to_string(commonAddress) + "-F_SC_NA_1-" + std::to_string(ioa);
945+
if (wait_for_response) {
946+
prepareCommandSuccess(cmdId, COMMAND_AWAIT_CON_TERM);
947+
}
948+
949+
// Create ASDU for directory request (F_SC_NA_1, Type 122)
950+
std::unique_lock<Module::GilAwareMutex> lock(connection_mutex);
951+
CS101_AppLayerParameters alParams =
952+
CS104_Connection_getAppLayerParameters(connection);
953+
954+
CS101_ASDU asdu =
955+
CS101_ASDU_create(alParams, false, CS101_COT_FILE_TRANSFER, 0,
956+
static_cast<int>(commonAddress), false, false);
957+
958+
// Create FileCallOrSelect information object
959+
// SCQ = 1 means "select file" (CS101_SCQ_SELECT_FILE)
960+
FileCallOrSelect io =
961+
FileCallOrSelect_create(NULL, static_cast<int>(ioa), CS101_NOF_TRANSPARENT_FILE, 0, 1);
962+
963+
CS101_ASDU_addInformationObject(asdu, (InformationObject)io);
964+
InformationObject_destroy((InformationObject)io);
965+
966+
bool const result = CS104_Connection_sendASDU(connection, asdu);
967+
lock.unlock();
968+
969+
CS101_ASDU_destroy(asdu);
970+
971+
if (wait_for_response) {
972+
if (result) {
973+
return awaitCommandSuccess(cmdId);
974+
}
975+
cancelCommandSuccess(cmdId);
976+
}
977+
return result;
978+
}
979+
935980
bool Connection::transmit(std::shared_ptr<Object::DataPoint> point,
936981
const CS101_CauseOfTransmission cause) {
937982
auto type = point->getType();

src/remote/Connection.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,17 @@ class Connection : public std::enable_shared_from_this<Connection> {
393393
bool test(std::uint_fast16_t commonAddress, bool with_time = true,
394394
bool wait_for_response = true);
395395

396+
/**
397+
* @brief select a file on remote server for transfer
398+
* @param commonAddress station address
399+
* @param ioa information object address of the file
400+
* @param wait_for_response blocking or non-blocking
401+
* @return success information
402+
*/
403+
bool fileSelect(std::uint_fast16_t commonAddress,
404+
std::uint_fast32_t ioa,
405+
bool wait_for_response = true);
406+
396407
/**
397408
* @brief transmit a command to a remote server
398409
* @param point control point

0 commit comments

Comments
 (0)